【原创翻译,转载请注明译者及原文链接】
原文链接:
http://effbot.org/zone/python-with-statement.htm
从comp.lang.python和其他论坛来看,即使对于有经验的python程序员,Python 2.5的新with语句也让人有一些迷惑。
但实质上,一旦你理解了它所想要解决的问题,相较于python的其他内容,with表达式其实是相当简单的。看下面的代码:
set things up try: do something finally: tear things down
在这里,“set things up”可以是打开一个文件,或者取得一些外部资源,而“tear things down”则将是关闭文件,或者释放,移除资源。try-finally的结构保证了“tear things down”这部分总会被执行,即使函数主体的代码不能正常结束。
如果你需要经常干这件事,那么把“set things up”和“tear things down”的代码放到一个库函数中并让其可以重复使用将会带来许多方便。当然你可以像这么干:
def controlled_execution(callback): set things up try: callback(thing) finally: tear things down def my_function(thing): do something controlled_execution(my_function)
但这样有些罗嗦,尤其是当需要修改本地变量的时候。另外一种方法是使用仅一次的generator,并且使用for-in语句来“包装”代码:
def controlled_execution(): set things up try: yield thing finally: tear things down for thing in controlled_execution(): do something with thing
但是yield在2.4及更早先的版本中甚至不允许被放到try-finally中。尽管这在2.5中被修正了,但这仍然显得有些古怪——你知道你仅仅想要执行某段语句一次,但你却使用了循环。
所以在尝试了很多方法之后,GvR和其python开发团队最终对后者进行了一个概括,使用对象而不是generator来控制外部代码的行为:
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: some code
现在,当“with”语句被执行时,Python会首先计算类实例化的表达式(controlled_execution()),并基于表达式的值调用__enter__方法(这也被称为“上下文保护”,"context guard"),并且把__enter__函数的返回值赋给as后面的变量。Python随后会执行代码主体,不管主体代码发生了什么,保护(guard)对象的__exit__方法都会被调用。
作为额外的奖励,__exit__方法还可以监测异常,并且根据需要阻止异常或者对异常进行处理。要阻止异常,返回一个为true的值就可以。例如,如下的__exit_方法不会放过任何TypeError,但会让其他异常通过。
def __exit__(self, type, value, traceback): return isinstance(value, TypeError)
在Python2.5中,文件对象被包装了__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
所以,打开一个文件,对其内容进行处理,并且确认关闭文件,你可以简单地这么干:
with open("x.txt") as f: data = f.read() do something with data
这并不是很难,不是吗?
转载请注明译者及原文链接:)