python中with as 用法

来源:互联网 发布:js可以做什么 编辑:程序博客网 时间:2024/05/16 17:21

注意,我写的东西和别人不一样,不是教科书式的。需要看冷静缩写版请去W3C谢谢。不想看口头语就出门左拐。

-------------------------------------------------------------------

不得不说。。。这个用法。。。。实在是。。。。太。。。。丧病了!!!!!!!!!

看了一下午都不是很清楚他怎么运作的。。。。我说各位写blog的大大们。。。。能说人话嘛。。。。。。。TAT


25.11.14补充:W3C讲得更清楚一些。


我先记录一下我的学习成果。。。。以防止以后忘记了。。。。。真是看了前面忘后面啊。。。。我勒个去。。。


-----------------华丽丽的分割线-----------------------

with as 呢,就是个python控制流语句,像 if 啊,while啊 一样。这个很好理解

那么当在网上搜 with as语句的时候呢,几乎每篇文章上来就说,这个语句啊,是简化版的try except finally语句,变得非常友好。。。。。我勒个擦!!友好泥煤啊。。。。try except finally是什么。。。有考虑过初学小白的心情吗。。。当然我就不说有些解释try finally语句的文章 上来就说with as语句。。。。。死循环了好吗。。。。。


呼。。。言归正传,那我们先理解一下try except finally语句是干啥的吧。。。

实际上呢,try/except语句和try/finally语句是两种语句,用于不同的场景。但是当二者结合在一起时,可以“实现稳定性和灵活性更好的设计”。很高大上的样子!!


1. try/except语句

用于处理程序执行过程中的异常情况,比如语法错误、从未定义变量上取值等等,也就是一些python货程序本身引发的异常、报错。比如你在python下面输入 1 / 0:


系统会给你一个ZeroDivisionError的报错。


说白了就是为了防止一些报错影响你的程序继续运行,就用try语句把他们抓出来(捕获)。

try/except的标准格式:

try:    ##normal blockexcept A:    ## exc A blockexcept:    ## exc other blockelse:    ## noError block
程序执行流程是:

-->执行normal block

-->发现有A错误,执行 exc A block(即处理异常)

-->结束

如果没有A错误呢?

-->执行normal block

-->发现B错误,开始寻找匹配B的异常处理方法,发现A,跳过,发现except others(即except:),执行exc other block

-->结束

如果没有错误呢?

-->执行normal block

-->全程没有错误,跳入else 执行noError block

-->结束


Tips: 我们发现,一旦跳入了某条except语句,就会执行相应的异常处理方法(block),执行完毕就会结束。不会再返回try的normal block继续执行了。(这里W3C没有说人话请注意,但意思一样。) 因此,如果想要一次发现多个错误,就得用try嵌套。(这个就深了,以后再补充)


举例(这段程序可以直接复制粘贴运行):

try:    a = 1 / 2 #a normal number/variable    print a    b = 1 / 0 # an abnormal number/variable    print b    c = 2 / 1 # a normal number/variable    print cexcept:    print "Error"

运行结果是啥呢?我们看一下


结果是,先打出了一个0,又打出了一个Error。就是把ZeroDivisionError错误捕获了。

程序怎么流转的呢?


先执行try后面这一堆语句,由上至下:

step1: a 正常,打印a. 于是打印出0 (因为a不是浮点型,0.5就会打印出0)

step2: b, 不正常了,0 不能做除数,所以这是一个错误。也别打印b了,直接跳到except报错去。于是打印了Error。

step3: 其实没有step3,因为程序结束了。c,是在错误发生之后的b语句后才出现,根本轮不到执行它。也就看不到打印出的c了


但这还不是try/except的所有用法

except后面还能跟表达式的! 所谓的表达式,就是错误的定义。也就是说,我们可以捕捉一些我们想要捕捉的异常。而不是什么异常都报出来。

异常分为两类: 

python标准异常

自定义异常。


我们先抛开自定义异常(因为涉及到类的概念),看看except都能捕捉到哪些python标准异常。

W3C有个总结表格:http://www.w3cschool.cc/python/python-exceptions.html  哈哈 偷懒结束。直接来个栗子吧!

try:    a = 1 / 2    print a    print m    b = 1 / 0    print b    c = 2 / 1    print cexcept NameError:    print "Ops!!"except ZeroDivisionError:    print "Wrong math!!"except:    print "Error"
输出结果:


我们看到,当程序执行到print m的时候 发现了一个NameError: name 'm' is not defined,于是控制流去寻找匹配的except异常处理语句。咦!发现了第一条匹配,执行对应block。执行完结束。


好了。那这个语句到底有什么应用场景呢?之前我觉得毫无用处啊! 系统有报错就报错嘛,把代码改好就行噜,干啥还要捕捉。不过当我知道可以自拟定异常的时候,就觉得。。。哦!!原来还是有用的。 比如,让一个人try取钱,如果他取的钱小于他的存款,那就else成功取出。如果要取的钱大于他的存款,那就报错。而这种错误系统并不能识别,因为可以把钱搞成负值....银行怎么能干呢?


不过自拟定错误怎么做呢?网上一堆例子全是用类,但是我真的还没学啦。。。。这部分我以后补充好了。。。。


2.try/finallly语句

用于无论执行过程中有没有异常,都要执行清场工作。比如try后面语句执行时报错了,Dont worry babe!! 直接执行finally语句即可哦!不会直接系统报错。


好的 现在我们看看他俩合在一起怎么用!!

<pre name="code" class="python">try:    execution block  ##正常执行模块except A:    exc A block ##发生A错误时执行except B:    exc B block ##发生B错误时执行except:    other block ##发生除了A,B错误以外的其他错误时执行else:    if no exception, jump to here ##没有错误时执行finally:    final block  ##总是执行

通过前面的解释,这段代码应该很好理解了。

tips: 注意顺序不能乱,否则会有语法错误

        except, else, finally 都不是必须的。注意排列组合。但如果用else就必须有except,否则会有语法错误。

来个栗子---

try:    a = 1 / 2    print a    print m    b = 1 / 0    print b    c = 2 / 1    print cexcept NameError:    print "Ops!!"except ZeroDivisionError:    print "Wrong math!!"except:    print "Error"else:    print "No error! yeah!"finally:    print "Successfully!"
执行结果


你也可以用上面代码注释掉几行看看会有什么不同的结果。


不过我也发现了一个问题:当缩进有错的时候(比如应该缩进四个space,结果打成了一个tab),缩进错误IndentationError捕捉不到。直接跳出了。好奇怪。。。

-----------------------------------------分割。。。分割。。。。--------------------------------------

try语句终于搞清楚了! 那么可以继续with as的探险了

with as 语句的结构如下

with expression [as variable]:    with-block

看这个结构我们可以获取至少两点信息 1. as可以省略 2. 有一个句块要执行

好了。那么这句话如何执行呢?容我先粘一段别人写的,然后再一点点解谜......

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

        from: http://blog.csdn.net/elevenqiao/article/details/6796653 

        with是一个控制流语句,跟if/for/while/try之类的是一类的,with可以用来简化try finally代码,看起来可以比try finally更清晰。
        这里新引入了一个"上下文管理协议"context management protocol,实现方法是为一个类定义__enter__和__exit__两个函数。
        with expresion as variable的执行过程是,首先执行__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。
        然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit()),在with-block执行完成后,会执行__exit__函数。
        这样的过程其实等价于:
        try:
        执行 __enter__的内容
        执行 with_block.
        finally:
        执行 __exit__内容
        只不过,现在把一部分代码封装成了__enter__函数,清理代码封装成__exit__函数。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

是不是有点儿晕了?。。哈哈 别捉急容我慢慢道来:

所谓上下文管理协议,其实是指with后面跟的expression。这个expression一般都是一个类的实体。这个类的实体里面要包含有对__enter__和__exit__函数的定义才行。

--那么,首先,什么是类呢? 类的定义好多书上都讲过,我就简单举个栗子:

   比如,公司员工,就是一个类咯。公司员工基本信息大家都一样,比如姓名,性别,岗位名称,工资,从属部门等等。我们就可以定义个类名叫做employee。除了给类定义一些上述属性之外,还可以定义类的方法。也就是允许对类的内容有哪些操作,比如可以对employee这个类添加门禁权限操作add_permission,或者加减工资操作review_salary等等。

    所以,我们其实可以意识到,比如aaa.txt这种文件对象,其实就是文件类的一个实例。我们可以打开它,阅读它,关闭它,是因为python里面定义了对这个类的属性和方法。或者举个更明白的例子: string。我们知道一个字符串"This is" 和另外一个字符串" a dog." 可以用加号+ 加起来变成"This is a dog." 是因为python定义了字符串str这个类,允许对这个类进行加操作。

    最直观的方法就是用dir()函数来看一个类的属性和方法。比如要查看字符串类有哪些属性和方法:


    我们能看到可以添加新的str可以计算它的长度,拆分字符串等等很多方法。


好了回过头来,这个with语句跟的expression所用到的类(好拗口),必须含有__enter__和__exit__方法才行。(所以,字符串类型看起来就不行咯!!) 类,除了python内置的,当然还可以自己定义咯! 所以除了expression表示一些含有这两种方法的内置类,还可以自己定义类,让他们含有__enter__和__exit__方法。


其实,所谓的方法,看到这里大家应该会有感觉,就像函数调用一样嘛! 是的,没错! 类的方法几乎和普通的函数一样,只有一点区别,那就是类的方法“必须有一个额外的第一个函数名称,但是在调用这个方法的时候并不为这个参数赋值,Python会提供。这个特别的变量指对象本身,按照惯例它的名称是self”---from《简明Python教程》


来个梨子

class Person:  #用class 定义类,Person是类名    def sayHi(self):  #定义一个类方法,self必须有        print "Hello, how are you?"p = Person()p. sayHi()

输出结果

Hello, how are you?


self就是这么用哒!---->用法就是木有用! 嘻嘻....


可能大家一直奇怪,为什么有的方法,比如上图中的‘split’ ‘title’什么的都没有下划线,而又有很多,,比如__enter__和__exit__要写下划线呢?

其实加下划线这种,是python的一种特殊方法,叫做魔术方法。这些个魔术方法,都是带有下划线的。魔术方法可以看成是建立python这些牛人提供给你的好用的内置方法,这样你自己定义出来的类,就可以和python内置的类,比如字符串、文件等等一样“高端”,而不是傻不拉几的。

我们再举个栗子:

a = "This is "b = "a dog."a+b

我们都能看出,返回的结果应该是 "This is a dog." 这是因为a,b都是字符串的实例,字符串这个类定义中含有__add__这个特殊的魔术方法,允许直接来个加号把两段文字连在一起。但如果木有这个加号魔术方法呢? 很有可能代码就会变成这样:

a = "This is "b = "a dog."c = str.add(a,b) #str.add是一个让a和b连在一起的函数print c

是不是很奇怪?

因此,我们可以看出,方法也是属性,就是可以干什么的属性,属性也是方法。融会贯通!!

关于魔术方法,可以看这个网页:http://pycoders-weekly-chinese.readthedocs.org/en/latest/issue6/a-guide-to-pythons-magic-methods.html


那么__enter__和__exit__是怎么用的方法呢?我们直接来看一个栗子好了。  


from http://pycoders-weekly-chinese.readthedocs.org/en/latest/issue6/a-guide-to-pythons-magic-methods.html

class Closer:def __init__(self, obj):    self.obj = objdef __enter__(self):    return self.obj # bound to targetdef __exit__(self, exception_type, exception_val, trace):    try:        self.obj.close()    except AttributeError: # obj isn't closable        print 'Not closable.'        return True # exception handled successfully
这个是在定义一个叫做Closer的类。我们来看看这个类里有啥:


1. 一个 __init__方法。__init__,大家也看出来了,这是个魔术方法。它的用途是,在这个类的一个对象(实例)被建立时,马上运行。也就是初始化(initiate)的作用。

2. 一个__enter__方法。专门用于with语句的一个魔术方法。它的返回值要直接返回给 with as语句中跟在as后面的variable。

3. 一个__exit__方法。用于with语句的一个魔术方法,我理解放在其他地方也行如果有用的话。当with as语句中with-block被执行或者终止后,这个类对象应该做什么。如果这个码块执行成功,则exception_type, exception_val, trace的输入值都是null。如果码块出错了,就会变成像try/except/finally语句一样,exception_type, exception_val, trace 这三个值系统会分配值。

---------------------------------------------------------

看到这里想必聪明的你肯定知道with as语句大概如何执行了吧?我们来总结一下

(from http://longzhiwen-478.blog.163.com/blog/static/2980974920124151356498/)

with expression as variable    with-block

的执行过程是

-->首先执行expression里面的__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。
-->然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit()),在with-block执行完成后,会执行expression中的__exit__函数。


这个和try finally函数有什么关系呢?其实,样的过程等价于:

#######

try:    执行 __enter__的内容    执行 with_block.finally:    执行 __exit__内容
#######

这,就是with as语法的精髓了!! 好了,让我们来找一个不错的李子更好的理解一下吧!! 网上有些李子太高大上,什么封装ip,关闭ftp之类。。。都还没到那步嘛。。。

找到两个不错的李子,来自http://python.42qu.com/11155501

1. 没有报错李子:

#!/usr/bin/env python# with_example01.pyclass Sample:    def __enter__(self):        print "In __enter__()"        return "Foo"     def __exit__(self, type, value, trace):        print "In __exit__()" def get_sample():    return Sample() with get_sample() as sample:    print "sample:", sample

我们来捋一下步骤如何执行的:

--> 调用get_sample()函数,返回Sample()类;

--> 执行Sample类中的__enter__()方法,打印"In__enter_()"字符串,并将字符串“Foo”赋值给as后面的sample变量;

--> 执行with-block码块,即打印"sample: %s"字符串,结果为"sample: Foo"

--> 执行with-block码块结束,返回Sample()类,执行类方法__exit__()。因为在执行with-block码块时并没有错误返回,所以type,value,trace这三个arguments都没有值。直接打印 print "In__exit__()"


结果真是这样吗? 输出结果如下:

In __enter__()
sample:  Foo
In __exit__()

看来我们的解释是正确的。


2. 有报错的李子

class Sample:    def __enter__(self):        return self     def __exit__(self, type, value, trace):        print "type:", type        print "value:", value        print "trace:", trace     def do_something(self):        bar = 1/0        return bar + 10 with Sample() as sample:    sample.do_something()

步骤:

--> 调用Sample()类,执行类方法__enter__(),返回值self也就是实例自己赋值给sample。即sample是Sample()的一个实例(对象);

-->执行with-block码块: 实例sample调用方法do_something(); 

-->执行do_something()第一行 bar = 1 / 0,发现ZeroDivisionError,直接结束with-block码块运行

-->执行类方法__exit__(),带入ZeroDivisionError的错误信息值,也就是type, value, trace,并打印他们。


输出结果:

type: <type 'exceptions.ZeroDivisionError'>
value: integer division or modulo by zero
trace: <traceback object at 0x024B8260>

Traceback (most recent call last):
  File "ex37_mo03.py", line 15, in <module>
    sample.do_something()
  File "ex37_mo03.py", line 11, in do_something
    bar = 1/0
ZeroDivisionError: integer division or modulo by zero

结果可以看出,确实打印出了需要的内容。并且返回了一个错误。

0 0