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

1
2
3
4
5
6
7
8
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  块外部, 如

1
2
3
4
5
6
7
8
def read_file():
   f = open('azusa', 'r')
  try:
    print ''.join(f.readlines())
  except:
    print 'error occurs while reading file'
  finally:
    f.close()

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

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

1
2
3
4
5
6
7
8
9
10
11
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  结构来解决, 如

1
2
3
4
5
6
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  后引用的值. 一个简单的例子是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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!')

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

1
2
3
4
5
6
7
8
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语句之前,先看看一段简单的代码吧

1
2
3
4
5
lock = threading.Lock()
...
lock.acquire()
elem = heapq.heappop(heap)
lock.release()

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

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

1
2
3
4
5
6
7
lock = threading.Lock()
...
lock.acquire()
try:
  elem = heapq.heappop(heap)
finally:
  lock.release()

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

1
2
3
4
lock = threading.Lock()
...
with lock:
  elem = heapq.heappop(heap)

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

1
2
with expression [as variable]:
  with-block

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

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

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

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

1
2
3
4
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也可以这样写:

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

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

1
2
3
4
5
6
7
8
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  块外部, 如

1
2
3
4
5
6
7
8
def read_file():
   f = open('azusa', 'r')
  try:
    print ''.join(f.readlines())
  except:
    print 'error occurs while reading file'
  finally:
    f.close()

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

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

1
2
3
4
5
6
7
8
9
10
11
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  结构来解决, 如

1
2
3
4
5
6
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  后引用的值. 一个简单的例子是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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!')

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

1
2
3
4
5
6
7
8
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语句之前,先看看一段简单的代码吧

1
2
3
4
5
lock = threading.Lock()
...
lock.acquire()
elem = heapq.heappop(heap)
lock.release()

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

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

1
2
3
4
5
6
7
lock = threading.Lock()
...
lock.acquire()
try:
  elem = heapq.heappop(heap)
finally:
  lock.release()

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

1
2
3
4
lock = threading.Lock()
...
with lock:
  elem = heapq.heappop(heap)

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

1
2
with expression [as variable]:
  with-block

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

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

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

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

1
2
3
4
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也可以这样写:

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

转载:简单介绍Python中的try和finally和with方法的更多相关文章

  1. 【转载】关于Python中的yield

    在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...

  2. python中执行shell命令的几个方法小结(转载)

    转载:http://www.jb51.net/article/55327.htm python中执行shell命令的几个方法小结 投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014- ...

  3. 简单说明Python中的装饰器的用法

    简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下   装饰器对与 ...

  4. 简单谈谈Python中的几种常见的数据类型

    简单谈谈Python中的几种常见的数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等 ...

  5. Python中执行系统命令常见的几种方法--转载

    Python中执行系统命令常见的几种方法 Python中执行系统命令常见的几种方法有: (1)os.system # 仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息 # 如果再命令行下执 ...

  6. Python中使用多进程来实现并行处理的方法小结

    进程和线程是计算机软件领域里很重要的概念,进程和线程有区别,也有着密切的联系,先来辨析一下这两个概念: 1.定义 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和 ...

  7. python中readline判断文件读取结束的方法

    注:内容来自网络 本文实例讲述了python中readline判断文件读取结束的方法.分享给大家供大家参考.具体分析如下: 大家知道,python中按行读取文件可以使用readline函数,下面现介绍 ...

  8. Python中转换角度为弧度的radians()方法

    Python中转换角度为弧度的radians()方法 这篇文章主要介绍了Python中转换角度为弧度的radians()方法,是Python入门中的基础知识,需要的朋友可以参考下 radians()方 ...

  9. Python中日期和时间格式化输出的方法

    本文转自:https://www.jb51.net/article/62518.htm 本文实例总结了python中日期和时间格式化输出的方法.分享给大家供大家参考.具体分析如下: python格式化 ...

随机推荐

  1. 【Backbone】简介

    1.Model 2.Collection 3.View 4.Router 5.History 6.Events http://addyosmani.github.io/backbone-fundame ...

  2. Swift用UIBezierPath来画圆角矩形、自定义多路径图形

    最好的特点就是可以自定义路径,设置圆角和描边都很方便,以下为代码和效果,均在playground中实现 1.首先实现一个圆角矩形,并对此路径描边,为其绘制一个轮廓. 1 2 3 4 5 6 7 8 9 ...

  3. 三星Galaxy S4(GT-I9500)获取ROOT权限教程(转)

    http://news.candou.com/2013-05-20/453695_1.shtml 获取root权限的方法

  4. Java中static、final用法小结

    一.final 1.final变量: 当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引 ...

  5. Servlet 3.0 新特性

    Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发 ...

  6. windows 8.1 在硬盘上创建扩展分区

    管理员-命令提示符-> diskpart    -->进入分区工具. list disk     -->显示电脑连接磁盘数. select disk *  -->选择编号为*的 ...

  7. 关于Android 访问权限设置

    我前几天在做同城交友网(www.niyuewo.com)与医药网(www.yiyaojing.com)时遇到的问题整理如下: Android开发应用程序时,如果应用程序需要访问网络权限,需要在 And ...

  8. 高性能HTTP加速器Varnish安装与配置

    导读 Varnish是一款高性能且开源的反向代理服务器和HTTP加速器,它采用了全新的软件体系结构,和现在的硬件体系配合紧密.下面就由我给大家简单说说他的安装与配置. 安装 安装pcre 如果没有安装 ...

  9. LINUX内核源代码情景分析

    http://pan.baidu.com/s/1sjIwswP

  10. Cable master(二分题 注意精度)

    Cable master Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 26596   Accepted: 5673 Des ...