简单介绍Python中的try和finally和with方法


Posted in Python onMay 05, 2015

用 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)

 用 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)

Python 相关文章推荐
python实现图片批量剪切示例
Mar 25 Python
python3.5 + PyQt5 +Eric6 实现的一个计算器代码
Mar 11 Python
Python使用smtp和pop简单收发邮件完整实例
Jan 09 Python
Numpy数据类型转换astype,dtype的方法
Jun 09 Python
pycharm 解除默认unittest模式的方法
Nov 30 Python
python+pyqt5实现KFC点餐收银系统
Jan 24 Python
一文了解Python并发编程的工程实现方法
May 31 Python
python 在某.py文件中调用其他.py内的函数的方法
Jun 25 Python
浅谈cv2.imread()和keras.preprocessing中的image.load_img()区别
Jun 12 Python
python用什么编辑器进行项目开发
Jun 17 Python
Pycharm自动添加文件头注释和函数注释参数的方法
Oct 23 Python
Scrapy+Selenium自动获取cookie爬取网易云音乐个人喜爱歌单
Feb 01 Python
python中的闭包用法实例详解
May 05 #Python
Python闭包实现计数器的方法
May 05 #Python
深入探究Python中变量的拷贝和作用域问题
May 05 #Python
Python使用metaclass实现Singleton模式的方法
May 05 #Python
python中查看变量内存地址的方法
May 05 #Python
Python中统计函数运行耗时的方法
May 05 #Python
Python调用命令行进度条的方法
May 05 #Python
You might like
Ext.data.PagingMemoryProxy分页一次性读取数据的实现代码
2010/04/07 PHP
深入extjs与php参数交互的详解
2013/06/25 PHP
php中isset与empty函数的困惑与用法分析
2019/07/05 PHP
js中parseFloat(参数1,参数2)定义和用法及注意事项
2013/01/27 Javascript
javascript中match函数的用法小结
2014/02/08 Javascript
js设置文本框中焦点位置在最后的示例代码(简单实用)
2014/03/04 Javascript
js获取鼠标点击的位置实现思路及代码
2014/05/09 Javascript
Javascript让DEDECMS告别手写Tag
2014/09/01 Javascript
js实现将选中值累加到文本框的方法
2015/08/12 Javascript
使用contextMenu插件实现Bootstrap table弹出右键菜单
2017/02/20 Javascript
AngularJS实现页面跳转后自动弹出对话框实例代码
2017/08/02 Javascript
原生JS实现移动端web轮播图详解(结合Tween算法造轮子)
2017/09/10 Javascript
seajs实现强制刷新本地缓存的方法分析
2017/10/16 Javascript
js canvas实现红包照片效果
2018/08/21 Javascript
浅谈Vue的响应式原理
2019/05/30 Javascript
d3.js实现图形拖拽
2019/12/19 Javascript
微信小程序实现翻牌抽奖动画
2020/09/21 Javascript
原生js实现俄罗斯方块
2020/10/20 Javascript
三剑客:offset、client和scroll还傻傻分不清?
2020/12/04 Javascript
python实现搜索本地文件信息写入文件的方法
2016/02/22 Python
python实现AES和RSA加解密的方法
2019/03/28 Python
基于Python2、Python3中reload()的不同用法介绍
2019/08/12 Python
Python函数基本使用原理详解
2020/03/19 Python
PyQt5事件处理之定时在控件上显示信息的代码
2020/03/25 Python
Mysql数据库反向生成Django里面的models指令方式
2020/05/18 Python
英国最大的奢侈品零售网络商城:Flannels
2016/09/16 全球购物
英国最大的美妆产品在线零售商之一:Beauty Bay
2017/09/29 全球购物
教师应聘自荐信范文
2014/03/14 职场文书
绿色城市实施方案
2014/03/19 职场文书
结婚主持人致辞
2015/07/28 职场文书
新年寄语2016
2015/08/17 职场文书
朋友圈早安励志语录!
2019/07/08 职场文书
温馨祝福晨语:美丽的一天从我的问候开始
2019/11/28 职场文书
python函数指定默认值的实例讲解
2021/03/29 Python
Windows server 2012 NTP时间同步的实现
2022/06/25 Servers
python缺失值填充方法示例代码
2022/12/24 Python