博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

with as 异常处理 contexlib上下文管理器

Posted on 2017-06-21 11:28  bw_0927  阅读(550)  评论(1)    收藏  举报

https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/index.html

 

file = open('demo.py', 'r', encoding='UTF-8')
try:
for line in file:
print(line, end='')
except:
print('讀取檔案發生錯誤')
finally:
file.close()


為了要處理檔案讀取過程中發生的例外,並且最後確定檔案一定會關閉,你使用了try..except...finally語句,實際上,在Python 3(或2.6)中,你可以使用with as語句來簡化程式的撰寫:

with open('demo.py', 'r', encoding='UTF-8') as file:
for line in file:
print(line, end='')


with之後的運算式傳回的物件,可以使用as指定給變數來參考,在上面的例子中,file所參考到的物件,最後會被自動關閉,即使在with as的區塊中發生了例外,最後一定會關閉file所參考的物件。

 

with 语句是从 Python 2.5 开始引入的一种与异常处理相关的功能

with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等

 

https://www.bwangel.me/2016/04/25/Python%E7%9A%84with%E8%AF%AD%E5%8F%A5/

With语句的通常用法

with语句是一个新的控制流结构, 它的基本结构如下:

with expression [as variable]:
  with-block


这个对象可能返回一个值,这个值可以绑定到一个命名变量variable(注意variable并不是表达式结果的赋值)。
expression
应该是可求值的,而且它的求值结果应该是一个支持上下文管理协议的对象。

variable能够在with-block语句执行前运行一些构造代码,且在with-block语句执行后运行一些析构代码,甚至就算with-block语句抛出异常了,析构代码一样能够运行。

一些Python标准对象已经支持上下文管理协议,且能够和with一起使用,例如File对象:

with open('/etc/passwd', 'r') as f:
  for line in f:
    print line
  ... more processing code ...


threading
模块的锁和条件变量也支持with语句:在这个语句执行后,文件对象f将会是关闭状态,就算for循环抛出了一个一场,只是部分执行了with-block的代码。

lock = threading.Lock()
with lock:
  # 原子操作的代码
  ...


这个锁在with-block代码执行之前被锁定,而且在with-block语句执行完以后总是被释放

 

 

====================================

http://www.bjhee.com/python-context.html

谈一谈Python的上下文管理器

经常在Python代码中看到with语句,仔细分析下,会发现这个with语句功能好强,可以自动关闭资源。这个在Python中叫上下文管理器Context Manager。那我们要怎么用它,什么时候用它呢。这里我们就来聊一聊。

上下文管理器的作用

很多情况,当我们使用完一个资源后,我们需要手动的关闭掉它,比如操作文件,建立数据库连接等。但是,在使用资源的过程中,如果遇到异常,很可能错误被直接抛出,导致来不及关闭资源。所以在大部分程序语言里,我们使用”try-finally”语句来确保资源会关闭。比如下面的Python写文件代码:

 

这样做固然没有问题,但是当”try-finally”中间的逻辑复杂,而且还带有各种嵌套的话,代码就很不容易维护。Python的with语句,可以说功能同上面的”try-finally”几乎一样,但代码看上去简洁的多,我们来实现同样的功能:

 

with语句后面跟着open()方法,如果它有返回值的话,可以使用as语句将其赋值给f。在with语句块退出时,”f.close()”方法会自动被调用,即使”f.write()”出现异常,也能确保close()方法被调用。

自定义类来使用上下文管理器

上例中”open()”方法是Python自带的,那我们怎么定义自己的类型来使用with语句呢。其实只要你的类定义了”__enter__()”和”__exit__()”方法,就可以使用Python的上下文管理器了。”__enter__()”方法会在with语句进入时被调用,其返回值会赋给as关键字后的变量;而”__exit__()”方法会在with语句块退出后自动被调用。

我们来实现个跟上节一样的文件写入功能:

 

异常处理

肯定有朋友注意到上面的”__exit__()”带了三个参数,是的,他们是用来异常处理的。大部分情况下,我们希望with语句中遇到的异常最后被抛出,但也有时候,我们想处理这些异常。”__exit__()”方法中的三个参数exc_type, exc_val, exc_tb分别代表异常类型,异常值,和异常的Traceback。当你处理完异常后,你可以让”__exit__()”方法返回True,此时该异常就会不会再被抛出。比如我们将上例中的”__exit__()”方法改一下:

 

 

现在,如果遇到SyntaxError的话,异常会被正常抛出,而其他异常的话都会被忽略。

contextlib模块

Python中还有一个contextlib模块提供一些简便的上下文管理器功能。

CLOSING()方法

如果说with语句块在退出时会自动调用”__exit__()”方法的话,那用了”contextlib.closing()”的with语句块则在退出时会自动调用”close()”方法。看一下示例:

 

程序运行后,会打印出

Open Resource
Close Resource

说明Resource类创建的对象被赋给了as关键字后面的变量r,而with语句块退出时,自动调用了”r.close()”方法。

CONTEXTMANAGER装饰器

“@contextlib.contextmanager”是一个装饰器,由它修饰的方法会有两部分构成,中间由yield关键字分开。由此方法创建的上下文管理器,在代码块执行前会先执行yield上面的语句;在代码块执行后会再执行yield下面的语句。看个例子比较容易明白:

 

 

这个”timeit()”方法实现了一个计时器,它会计算由他生成的with语句块执行时间。可以看出,yield上面的语句就如同之间介绍过的”__enter__()”方法,而yield下面的语句就如同”__exit__()”方法。而yield部分就是with语句块中的代码。如果yield后面带参数的话,我们就可以用as关键字赋值给后面的变量,比如上例:

 

 

需要注意的是,”@contextlib.contextmanager”不像之前介绍的”__exit__()”方法,遇到异常也会执行。也就是with语句块抛出异常的话,yield后面的代码将不会被执行。所以,必要时你需要对yield语句使用”try-finally”。