简单介绍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递归函数和河内塔问题
Apr 18 Python
Python学习入门之区块链详解
Jul 25 Python
python实现冒泡排序算法的两种方法
Mar 10 Python
python中将一个全部为int的list 转化为str的list方法
Apr 09 Python
Python基于递归算法实现的汉诺塔与Fibonacci数列示例
Apr 18 Python
Python装饰器限制函数运行时间超时则退出执行
Apr 09 Python
Python中typing模块与类型注解的使用方法
Aug 05 Python
python实现本地批量ping多个IP的方法示例
Aug 07 Python
Python OpenCV实现鼠标画框效果
Aug 19 Python
python GUI库图形界面开发之PyQt5窗口布局控件QStackedWidget详细使用方法
Feb 27 Python
Python tkinter 下拉日历控件代码
Mar 04 Python
Python如何转换字符串大小写
Jun 04 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
用php解析html的实现代码
2011/08/08 PHP
纯PHP生成的一个树叶图片画图例子
2014/04/16 PHP
php中使用getimagesize获取图片、flash等文件的尺寸信息实例
2014/04/29 PHP
PHP实现采集中国天气网未来7天天气
2014/10/15 PHP
Win10 下安装配置IIS + MySQL + nginx + php7.1.7
2017/08/04 PHP
PHP析构函数destruct与垃圾回收机制的讲解
2019/03/22 PHP
PHP 构造函数和析构函数原理与用法分析
2020/04/21 PHP
PHP7生产环境队列Beanstalkd用法详解
2020/05/19 PHP
基于jquery用于查询操作的实现代码
2010/05/10 Javascript
javascript函数定义的几种区别小结
2014/01/06 Javascript
javascript下拉框选项单击事件的例子分享
2015/03/04 Javascript
JavaScript的Polymer框架中dom-repeat与VM的相关操作
2015/07/29 Javascript
不得不分享的JavaScript常用方法函数集(下)
2015/12/25 Javascript
分类解析jQuery选择器
2016/11/23 Javascript
微信小程序开发之大转盘 仿天猫超市抽奖实例
2016/12/08 Javascript
Bootstrap中glyphicons-halflings-regular.woff字体报404错notfound的解决方法
2017/01/19 Javascript
HTML5实现微信拍摄上传照片功能
2017/04/21 Javascript
JS实现的检验身份证格式并输出出生日期,年龄,性别,出生地示例
2019/05/17 Javascript
Vue时间轴 vue-light-timeline的用法说明
2020/10/29 Javascript
python中子类继承父类的__init__方法实例
2016/12/15 Python
python DataFrame获取行数、列数、索引及第几行第几列的值方法
2018/04/08 Python
python pandas 组内排序、单组排序、标号的实例
2018/04/12 Python
Tesserocr库的正确安装方式
2018/10/19 Python
python中pytest收集用例规则与运行指定用例详解
2019/06/27 Python
Anaconda3+tensorflow2.0.0+PyCharm安装与环境搭建(图文)
2020/02/18 Python
Python基础之列表常见操作经典实例详解
2020/02/26 Python
Ootori在线按摩椅店:一家专业的按摩椅制造商
2019/04/10 全球购物
全球最大的瓷器、水晶和银器零售商:Replacements
2020/06/15 全球购物
小学教师自我鉴定
2013/11/07 职场文书
英文商务邀请信
2014/01/22 职场文书
孝老爱亲模范事迹
2014/01/24 职场文书
个人授权委托书
2014/04/03 职场文书
党员干部观看《周恩来四个昼夜》思想汇报
2014/09/10 职场文书
母亲节寄语大全
2015/02/27 职场文书
关于Mybatis中SQL节点的深入解析
2022/03/19 Java/Android
MySQL 原理与优化之原数据锁的应用
2022/08/14 MySQL