理解Python 中的 with 语句

一、with 语句是什么?

当你遇到一些事前先要进行设置,事后要进行清理的情形时,Python的with语句为你提供了一个非常方便的方式。一个非常好的例子就是文件操作,你首先要获得文件句柄,然后通过文件句柄来读取文件内容,最后还要关闭文件。


如果不使用with 语句,你可能会这样来进行文件的操作:

file = open("/tmp/foo.txt")
data = file.read()
file.close()


这里有两个问题。一是你可能会忘记关闭文件;二是出现异常的时候怎么关闭文件。可以通过下面的方式来解决这两个问题。

file=open("/tmp/foo.txt")
try:
    data =file.read()
finally:
    file.close()


虽然上面的代码能够很好的运行,但是它太冗长了。这时候with语句就有用武之地了。它除了具有简洁的语法外,还能很好的处理异常。上面的代码用with语句来实现是这样的:

with open("/tmp/foo.txt") as file:
   data =file.read()


2、with 语句的工作原理是什么?

这看起像是魔法,但是Python的with语句却是比魔法高明多了。基本的思想是with语句后面的表达式所求的值需要有__enter__() 和 __exit__() 方法。

with语句后面的表达式被求值后会得到一个对象,接着会该对象的__enter__()方法会被自动调用;__enter__()方法的返回值会被赋值给跟着as后面的变量。当所有with语句块内的代码被执行后,__exit__()方法会被自动调用。

这个过程可以用下面这个例子来说明:

#!/usr/bin/env python
# with_example01.py
 
 
class 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

执行with语句的结果如下:

bash-3.2$ ./with_example01.py
In __enter__()
sample: Foo
In __exit__()

从上面可以知道:

1、__enter__方法被执行了

2、__enter__方法的返回值被赋值给as后面的变量了;在这个例子中是”Foo”赋值给sample

3、执行with语句代码块,打印出sample 值为 Foo

4、__exit__方法被调用

 

With 语句真正强大的地方在于它能够处理异常。你可能也注意到了,sample的__exit__方法有三个参数:val、type和trace。这些参数在处理异常时非常有用。让我们通过修改上面的代码来看看它是怎么做到的。

#!/usr/bin/env python
# with_example02.py
 
 
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()

注意看这个例子,with语句后面用了Sample()而不是get_sample()。不过这并没有什么影响,只要with语句后面的表达式返回的对象有__enter__() 和 __exit__() 方法就可以了。在这个例子中,Sample的 __enter__()方法返回了新创建的Sample实例,并将其赋值给as后面的sample变量。

代码执行结果如下:

bash-3.2$ ./with_example02.py
type: <type'exceptions.ZeroDivisionError'>
value: integer division or modulo by zero
trace: <traceback object at 0x1004a8128>
Traceback (most recent call last):
  File "./with_example02.py", line 19, in <module>
    sample.do_something()
  File "./with_example02.py", line 15, in do_something
    bar= 1/0
ZeroDivisionError: integer division or modulo by zero

事实上,无论出现什么情况__exit__方法都会被执行,即使在with语句代码块内有异常被抛出。从上面你可以看到,被抛出的异常的类型,值和追踪栈都会被传递给__exit__这个方法。在上面的例子中,一个ZeroDivisionError被抛出。因此在开发库的时候,可以将一些清理资源、关闭文件等操作放在__exit__方法中。

 

总而言之,Python的with语句提供了更加简洁的代码,并使得出现异常时,一些处理操作更加简单方便。



原文:https://sdqali.in/blog/2012/07/09/understanding-pythons-with-statement/



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值