Hom's Blog


Python:with statement

经常看到 with open('filename') as f:.... 的用法. 就是打开文件嘛. 和平时的有啥不同呢? with语句是干嘛的??

with语句是Python2.5以后引进的, 主要是作为一种特殊的try...finally..来处理对象, 需要联系上两个特殊方法: __enter____exit__. 他又叫做上下文管理协议(with语句).


  • 一般地, 我们要处理一些事,为防漏掉或者出错, 有时必须做些手尾功夫, 这时经常用到try .. finally.... 例如
f=open('hi.txt')
try:
	#some codes
	os.renames('hello.txt','hi2.txt')
finally:
	f.close()

 #set things up
try:
    #do something
finally:
    #tear things down

上面的写法可以避免中间代码执行时出错 (例如不存在hello.txt或者已存在hi2.txt 或者别的状况) 忘了关闭文件. 类似地, 我们建立一些东西, 最后要销毁他. 也是这么回事.

  • 如果要经常上述那么干, 可以包装到函数里面, 例如:
def controlled_execution(callback):
    #set things up
    try:
        callback(thing)
    finally:
        #tear things down

def my_function(thing):
    #do something

controlled_execution(my_function)

我们将上面的#some codes和改名放到my_function, 而打开hi.txt文件操作放到controlled_execution, 这样就可以反复利用这个try..finally文件操作了.

如果我们要返回一个对象并且该对象作为局部变量可能将会被修改, 用完后还是这样tear收拾掉. 可以:

def controlled_execution():
    #set things up
    try:
        yield thing
    finally:
        #tear things down

for thing in controlled_execution():
    #do something with thing

这里需要使用for循环来生成该对象..怪怪的..例如上面的例子, 我们将hi.txt的文件对象返回并被操作, 就要这么干…为了简化这个 进去干点什么甚至返回值, 最后还要撕毁掉 的过错, 引入了两个新的类的特殊方法:__enter____exit__以及with语句, 一个是进去时干点什么(可以生成返回), 一个是退出时撕毁掉. 例如:

class controlled_execution:
    def __enter__(self):
        #set things up
        return thing
    def __exit__(self, type, value, traceback):
        #tear things down

with controlled_execution() as thing:
     #do something with thing

__enter__ 相当于之前的设置并try的过程, __exit__相当于finally的过程, 而 with ... as var 则相当于利用该类两个方法并可以返回对象给as后的变量. 这个with相当于上上面例子的controlled_execution函数的打包.

with语句的执行过程是:

  1. with语句并调用时, 后面的表达式会被执行, 表达式的返回值(例如文件对象)的__enter__方法会被调用
  2. __enter__调用的返回结果递给as后的变量, 用于日后执行操作
  3. 随后会执行with内的语句块block (#do something with thing), 如果出了问题, 就执行__exit__部分(相当于finally)
  4. 如果没有出错, 最后语句块结束时, 也会执行__exit__部分.

__exit__部分可以接受参数, 分别是异常类型、异常值和追溯信息(如果有的话). 在异常发生时会自动传给__exit__方法, 方便处理 (例如忽略某种异常, 返回一些值). 例如:

def __exit__(self, type, value, traceback):
    return isinstance(value, TypeError)

从上面可以知道, 使用with语句需要几个条件 (如创建可以使用with的类时要注意):

  • with后的表达式返回一个对象
  • 对象含有__enter____exit__ 方法
  • 一般这个对象要被进一步被处理, 这时在__enter__中要使用return 语句来返回给as后的变量(返回自身就 return self, 还可以使用生成器生成别的玩意)

一定要注意这三个条件才能写出with的使用对象!

文件对象的with

文件对象自带__enter____exit__ 方法, 前者返回文件对象自身, 后者就是关闭文件咯.

>>> f = open("x.txt")
>>> f
<open file 'x.txt', mode 'r' at 0x00AE82F0>
>>> f.__enter__()
<open file 'x.txt', mode 'r' at 0x00AE82F0>
>>> f.read(1)
'X'
>>> f.__exit__(None, None, None)
>>> f.read(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file

使用时就可以简化为下面的语句, 打开的文件对象被返回为as后的变量, 随后文件被操作, 最后不用写关闭自动关闭文件:

with open("x.txt") as f:
    data = f.read()
    #do something with data

这种写法的好处是把原来的 f=open(); try: .. finally: f.close() 简化为一行了~

Reference

  1. 让对象支持上下文管理协议


◆ 本文地址: http://platinhom.github.io/2016/02/03/py-with-statement/, 转载请注明 ◆

前一篇: Selenium控制Chrome初探
后一篇: Github升级Jekyll3.0-强制使用rouge语法高亮


Contact: Hom / 已阅读()
Source 类别: Coding  标签: Python