python with statement

这篇文章主要对工作中经常接触到python 文件读写及with语句做一个总结.

文件打开

try:
    with open(filename,'r') as filea:
        do_process()
except IOError as e:
    print e.strerrpr

打开多个文件:

try:
    with open(file1,'r') as filea,open(file2,'r') as fileb:
        do_process()
except IOErrpr as e:
    print e.strerrpr

为什么用with语句来打开文件?我们先看下面一段代码。

try:
    f=open("test.txt",'r')
    print f.read()
except IOError as e:
    print e
finally:
    f.close()

这里我们使用了try ... finally语句。无论try子句是否有异常或错误产生,即使执行break或sys.exit(), finally子句最终一定会被执行。上面的代码,能够确保打开的文件,最后一定能被关闭。在编写socket程序, 数据库程序时,都可以用try...finally语句来释放回收各种资源(python官方文档中,称作定义清理行为)。

但是上面的代码显得不够Pythonic,这里我们介绍with语句,with语句也是一种控制流语句,可以简化 try...finally语句。在深入介绍with语句之前我们先熟悉一下会话管理协议,会话管理协议包含 __enter__和__exit__两个方法,实现了__enter__和__exit__这两个方法的对象便是 一个会话管理器。下面是一个简单的会话管理器的代码:

class ContextDemo():
    def __enter__(self):
        print "enter context"
    def __exit__ (self, type, value, traceback):
        print "exit context"
        return True

要使用这个会话管理器,用下面的两行代码就可以了

with ContextDemo() as context:  #这里是with表达式
    print "do something"        #这里是with-body语句块

执行之后输出如下:

enter context
do something
exit context

我们以上面的Demo为例进行分析,with语句执行时先进入__enter__方法中,并将__enter__的返回值保存在as后面的变量里。 然后程序执行 print "do somthing" 这条语句,这部分的语句也叫做with-body语句块。 with-body部分的语句块执行完毕后程序会自动执行 __exit__这个方法里的代码。 无论with-body部分的代码有无异常,即使调用sys.exit()。__exit__这部分代码也会被执行。

在需要关闭socket或者文件的地方,我们可以实现自己的会话管理器,用with语句简化代码的输入。 幸运的是python对一些内建对象加上了会话管理器的支持,以支持with表达式。例如上面提到的文件对象, 就已经实现了__enter__和__exit__方法。下面的代码是测试时的结果, 可以看出在执行了__exit__之后,文件便被关闭了。

Python 2.7.8 (default, Oct 20 2014, 15:05:29)
[GCC 4.9.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f=open('py.md')
>>> f
<open file 'py.md', mode 'r' at 0xb7502020>
>>> f.__enter__()
<open file 'py.md', mode 'r' at 0xb7502020>
>>> f.__exit__()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file

所以当我们打开文件时,可以用with表达式,而无需再关心文件是否关闭,这样的代码更加的简练。易懂。

对于with语句这里还有几点补充的:

1.as语句是可选的,如果__enter__函数没有返回值,as及其后的变量可以省略

2.当__exit__函数的几个参数得到的值不为None时,表示with-body的代码中有异常产生。可以在__exit__函数中返回True,忽略这个异常, 当返回False时,异常不会被忽略