2019独角兽企业重金招聘Python工程师标准>>>

用 Python 做一件很平常的事情: 打开文件, 逐行读入, 最后关掉文件; 进一步的需求是, 这也许是程序中一个可选的功能, 如果有任何问题, 比如文件无法打开, 或是读取出错, 那么在函数内需要捕获所有异常, 输出一行警告并退出. 代码可能一开始看起来是这样的

def read_file(): try: f = open('yui', 'r') print ''.join(f.readlines()) except: print 'error occurs while reading file' finally: f.close()

不过这显然无法运作, 因为  f  是在  try  块中定义的, 而在  finally  中无法引用.

如果将  f  提取到  try  块外部, 如

def read_file(): f = open('azusa', 'r') try: print ''.join(f.readlines()) except: print 'error occurs while reading file' finally: f.close()

那么, 问题在于当打开文件失败, 抛出异常将不会被捕获.

挫一点的方法自然是, 再套一层  try  吧

def read_file(): try: f = open('sawako', 'r') try: print ''.join(f.readlines()) except: print 'error occurs while reading file' finally: f.close() except: print 'error occurs while reading file'

当然这不仅仅是多一层缩进挫了, 连警告输出都白白多一次呢.

正规一点的方式是, 使用 Python 引入的  with  结构来解决, 如

def readFile(): try: with open('mio', 'r')  as f: print ''.join(f.readlines()) except: print 'error occurs while reading file'

当文件打开失败时, 异常自然会被  except  到; 否则, 在  with  块结束之后, 打开的文件将自动关闭.

除了打开文件, 还有其它这样可以用于  with  的东西么? 或者说, 怎么自定义一个什么东西, 让它能用于  with 呢?
    直接回答后一个问题吧, 秘密在于 Python 虚拟机在  with  块退出时会去寻找对象的  __exit__  方法并调用它, 把释放资源的动作放在这个  __exit__  函数中就可以了; 另外, 对象还需要一个  __enter__  函数, 当进入  with 块时, 这个函数被调用, 而它的返回值将作为  as  后引用的值. 一个简单的例子是

class Test: def __init__(self): print 'init' def __enter__(self): print 'enter' return self def __exit__(self, except_type, except_obj, tb): print except_type print except_obj import traceback print ''.join(traceback.format_tb(tb)) print 'exit' return True with Test() as t: raise ValueError('kon!')

执行这一段代码, 输出将会是

init
enter
<type 'exceptions.ValueError'>
kon! File "test.py", line 17, in <module> raise ValueError('kon!') exit

__exit__  函数接受三个参数, 分别是异常对象类型, 异常对象和调用栈. 如果  with  块正常退出, 那么这些参数将都是  None . 返回  True  表示发生的异常已被处理, 不再继续向外抛出.

简单的介绍到此为止, 详细的情况可以参考  PEP 343  (这数字真不错, 7 3).

下面介绍下 with 语句的实例用法 & 高级用法:

Python高端、大气、上档次的with语句

在说with语句之前,先看看一段简单的代码吧

lock = threading.Lock()
...
lock.acquire()
elem = heapq.heappop(heap)
lock.release()

很简单直观,多个线程共用一个优先级队列的时候,首先先用互斥锁lock.acquire()把优先级队列锁上,然后取元素,再然后lock.release()释放这个锁。

虽然看似非常符合逻辑的一个过程,但是里面隐藏着一个巨大的bug:当heap里面没有元素的时候,会抛出一个IndexError异常,再然后堆栈回滚,再然后lock.release()根本不会执行,这个锁就永远得不到释放,因此就发生了喜闻乐见的死锁问题。这个也是很多大神们讨厌异常的原因。经典Java风格的解决方案就是

lock = threading.Lock()
...
lock.acquire()
try:elem = heapq.heappop(heap)
finally:lock.release()

这个虽然可以,但是怎么看怎么dirty,和Python优雅、简单的风格出入很大。其实,自从Python2.5开始引入了with语句,一切就变得非常简单:

lock = threading.Lock()
...
with lock:elem = heapq.heappop(heap)

在此无论以何种方式离开with语句的代码块,锁都会被释放。
with语句的设计目的就是为了使得之前需要通过try...finally解决的清理资源问题变得简单、清晰,它的的用法是

with expression [as variable]:with-block

其中expression返回一个叫做「context manager」的对象,然后这个对象被赋给variable(如果有的话)。「context manager」对象有两个方法,分别是__enter__()和__exit__(),很明显一个在进入with-block时调用,一个离开with-block的时候调用。

这样的对象不需要自己去实现,在Python标准库里面很多API都是已经实现了这两个方法,最常见的一个例子就是读写文件的open语句。

with open('1.txt', encoding = 'utf-8') as fp:lines = fp.readlines()

无论是正常离开还是因为异常原因离开with语句块,打开的文件资源总是会释放。
接下去讨论一下with语句配合contextlib库的一些比较实用的方法,比如需要同时打开两个文件,一个读一个写,这个时候就可以这样写:

from contextlib import nested
...
with nested(open('in.txt'), open('out.txt', 'w')) as (fp_in, fp_out):...

这样就可以省掉两个with的语句的嵌套了,另外如果遇到一些还没有支持「context manager」的API呢?比如urllib.request.urlopen(),这个返回的对象因为不是「context manager」,结束的时候还需要自己去调用close方法。
类似这种API,contextlib提供了一个叫做closing方法,它会在离开with语句的时候,自动调用对象的close方法,因此urlopen也可以这样写:

from contextlib import closing
...
with closing(urllib.request.urlopen('http://www.yahoo.com')) as f:for line in f:sys.stdout.write(line)

参考资料:

[1] 8 PEP 343: The 'with' statement

https://docs.python.org/release/2.5/whatsnew/pep-343.html

Refer:http://ling0322.info/2013/10/03/python-with-statement.html

[2] Python 上下文管理器

http://python.jobbole.com/82289/

转载于:https://my.oschina.net/leejun2005/blog/145423

Python: try finally 与 上下文管理器简介相关推荐

  1. python中的with上下文管理器

    with 语句 with 语句是 Pyhton 提供的一种简化语法,确保不管使用过程中是否发生异常都会执行必要的"清理"操作,释放资源. 在没有学习with的句法之前,通常我们都是 ...

  2. Python基础教程:上下文管理器 context manager(with...as...)

    一.概念 上下文管理器:就是实现了上下文管理协议的对象.主要用于保存和恢复各种全局状态,关闭文件等.上下文管理器本身是一种装饰器. 上下文允许可以自动的开始和结束一些和事情.例如当利用with-as打 ...

  3. python with关键字_完全理解Python关键字with与上下文管理器

    如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 "with" 关键字的语句,它通常用在什么场景呢?今天就来说说 with 和 上下文管理器. 对于系统资源如文件.数据 ...

  4. python 多态 锁_python 上下文管理器,多态,数据锁定与自省,

    python 上下文管理器,多态,数据锁定与自省, 前文课题 通过装饰器来实现单例模式 通过类实现一个通用装饰器,皆可以装饰函数也可装饰类,即可有参也可无参 描述 new str repr call ...

  5. python上下文是什么_(1条消息)python使用@contextmanager来定义上下文管理器(一篇文章,彻底明白!码文并茂,简单明了)...

    什么是上下文管理器? 上下文管理器是在Python2.5之后加入的功能,可以在方便的需要的时候比较精确地分配和释放资源, with便是上下文管理器的最广泛的应用 with open("tes ...

  6. python使用@contextmanager来定义上下文管理器(一篇文章,彻底明白!码文并茂,简单明了)和 yield 和 __enter__ 和 __exit__

    什么是上下文管理器? 上下文管理器是在Python2.5之后加入的功能,可以在方便的需要的时候比较精确地分配和释放资源, with便是上下文管理器的最广泛的应用 with open("tes ...

  7. python 什么是上下文管理器(ContextManager)?

    上下文是 context 直译的叫法,在程序中用来表示代码执行过程中所处的前后环境.上下文管理器中有 enter 和 exit 两个方法,以with为例子,enter 方法会在执行 with 后面的语 ...

  8. python语言使用什么语句实现上下文管理协议_Python 上下文管理器

    上下文管理器 在使用Python编程中,可以会经常碰到这种情况:有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作:当语句块执行完成后,需要继续执行一些收尾动作. 例如:当需要操作文件或数 ...

  9. Python 上下文管理器和 with 语句

    1. 上下文管理器概念 什么是 Python 的上下文管理器(Context Managers)呢? ​ 含有 __enter__ 和 __exit__ 方法的对象就是.上下文管理器存在的目的是管理 ...

最新文章

  1. C语言经典例97-输入字符写入文件
  2. Android Studio编译提示如下attribute layout_constraintBottom_toBottomOf (aka com.luck.pictureselector:layou
  3. 【Uva - 10935】 Throwing cards away I (既然是I,看来还有Ⅱ、Ⅲ、Ⅳ?)(站队问题队列问题)
  4. pycharm安装javascript插件_IDEA必备插件系列-Rainbow
  5. redis 缓存有效期
  6. Spark SQL将rdd转换为数据集-以编程方式指定模式(Programmatically Specifying the Schema)
  7. MinGW-w64离线安装包,环境配置(Windows)
  8. HDU 4069 Squiggly Sudoku
  9. c语言进行catia二次开发,想入门CATIA二次开发CAA的盆友们(谈谈开发经验,或许对你有帮助)...
  10. Android 超级轻量的版本更新库AppUpdate
  11. arduino红外遥控控制小灯
  12. 云端敏捷部署单节点MySQl与Redis服务(以Ubuntu为例)
  13. ubuntu安装zsh及环境配置
  14. Java版 熄灯问题 debug调试版本
  15. Mysql MATCH() AGAINST() (MATCH AGAINST)
  16. Python基础——csv文件中某列数据替换为数字
  17. Java并发系列(10)——FutureTask 和 CompletionService
  18. 基于人脸识别的门禁系统设计
  19. 《HeadFirst设计模式》读书笔记-第9章v2-组合模式
  20. 【自然语言处理(NLP)】基于SQuAD的机器阅读理解

热门文章

  1. mysql 126_MySQL教程126-MySQL事务隔离级别
  2. linux卸载gdb命令,【Linux学习】GDB调试器基本命令必知必会(一)
  3. sqllite查询数据量_详解SQLite中的查询规划器
  4. 【[转】MySql模糊查询
  5. Shell Scipt 命令行带参数,输出log
  6. ffmpeg相关资源
  7. 一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(1)--创建和使用可重用工作流...
  8. PHP操作Mongodb API 及使用类 封装好的MongoDB操作类
  9. python 报ImportError: Install xlrd = 1.0.0 for Excel support错误出现及其解决帮法
  10. 面对挫折:给即将或已经遭受社会毒打的每一个人