简单介绍Python中的try和finally和with方法
用Python做一件很平常的事情:打开文件,逐行读入,最后关掉文件;进一步的需求是,这也许是程序中一个可选的功能,如果有任何问题,比如文件无法打开,或是读取出错,那么在函数内需要捕获所有异常,输出一行警告并退出.代码可能一开始看起来是这样的
 
defread_file():
try:
f=open('yui','r')
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
finally:
f.close()
不过这显然无法运作,因为 f 是在 try 块中定义的,而在 finally 中无法引用.
   如果将 f 提取到 try 块外部,如
 
defread_file():
f=open('azusa','r')
try:
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
finally:
f.close()
那么,问题在于当打开文件失败,抛出异常将不会被捕获.
   挫一点的方法自然是,再套一层 try 吧
 
defread_file():
try:
f=open('sawako','r')
try:
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
finally:
f.close()
except:
print'erroroccurswhilereadingfile'
当然这不仅仅是多一层缩进挫了,连警告输出都白白多一次呢.
   正规一点的方式是,使用Python引入的 with 结构来解决,如
 
defreadFile():
try:
withopen('mio','r')asf:
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
当文件打开失败时,异常自然会被 except 到;否则,在 with 块结束之后,打开的文件将自动关闭.
   除了打开文件,还有其它这样可以用于 with 的东西么?或者说,怎么自定义一个什么东西,让它能用于 with呢?
   直接回答后一个问题吧,秘密在于Python虚拟机在 with 块退出时会去寻找对象的 __exit__ 方法并调用它,把释放资源的动作放在这个 __exit__ 函数中就可以了;另外,对象还需要一个 __enter__ 函数,当进入 with块时,这个函数被调用,而它的返回值将作为 as 后引用的值.一个简单的例子是
 
classTest:
def__init__(self):
print'init'
def__enter__(self):
print'enter'
returnself
def__exit__(self,except_type,except_obj,tb):
printexcept_type
printexcept_obj
importtraceback
print''.join(traceback.format_tb(tb))
print'exit'
returnTrue
withTest()ast:
raiseValueError('kon!')
   执行这一段代码,输出将会是
 
init
enter
<type'exceptions.ValueError'>
kon!
File"test.py",line17,in<module>
raiseValueError('kon!')
exit
__exit__ 函数接受三个参数,分别是异常对象类型,异常对象和调用栈.如果 with 块正常退出,那么这些参数将都是 None.返回 True 表示发生的异常已被处理,不再继续向外抛出.
简单的介绍到此为止,详细的情况可以参考 PEP343 (这数字真不错,73).
下面介绍下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() ... withlock: elem=heapq.heappop(heap)
在此无论以何种方式离开with语句的代码块,锁都会被释放。
with语句的设计目的就是为了使得之前需要通过try...finally解决的清理资源问题变得简单、清晰,它的的用法是
 
withexpression[asvariable]: with-block
其中expression返回一个叫做「contextmanager」的对象,然后这个对象被赋给variable(如果有的话)。「contextmanager」对象有两个方法,分别是__enter__()和__exit__(),很明显一个在进入with-block时调用,一个离开with-block的时候调用。
这样的对象不需要自己去实现,在Python标准库里面很多API都是已经实现了这两个方法,最常见的一个例子就是读写文件的open语句。
 
withopen('1.txt',encoding='utf-8')asfp:
lines=fp.readlines()
无论是正常离开还是因为异常原因离开with语句块,打开的文件资源总是会释放。
接下去讨论一下with语句配合contextlib库的一些比较实用的方法,比如需要同时打开两个文件,一个读一个写,这个时候就可以这样写:
 
fromcontextlibimportnested
...
withnested(open('in.txt'),open('out.txt','w'))as(fp_in,fp_out):
...
这样就可以省掉两个with的语句的嵌套了,另外如果遇到一些还没有支持「contextmanager」的API呢?比如urllib.request.urlopen(),这个返回的对象因为不是「contextmanager」,结束的时候还需要自己去调用close方法。
类似这种API,contextlib提供了一个叫做closing方法,它会在离开with语句的时候,自动调用对象的close方法,因此urlopen也可以这样写:
 
fromcontextlibimportclosing
...
withclosing(urllib.request.urlopen('http://www.yahoo.com'))asf:
forlineinf:
sys.stdout.write(line)
 用Python做一件很平常的事情:打开文件,逐行读入,最后关掉文件;进一步的需求是,这也许是程序中一个可选的功能,如果有任何问题,比如文件无法打开,或是读取出错,那么在函数内需要捕获所有异常,输出一行警告并退出.代码可能一开始看起来是这样的
 
defread_file():
try:
f=open('yui','r')
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
finally:
f.close()
不过这显然无法运作,因为 f 是在 try 块中定义的,而在 finally 中无法引用.
   如果将 f 提取到 try 块外部,如
 
defread_file():
f=open('azusa','r')
try:
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
finally:
f.close()
那么,问题在于当打开文件失败,抛出异常将不会被捕获.
   挫一点的方法自然是,再套一层 try 吧
 
defread_file():
try:
f=open('sawako','r')
try:
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
finally:
f.close()
except:
print'erroroccurswhilereadingfile'
当然这不仅仅是多一层缩进挫了,连警告输出都白白多一次呢.
   正规一点的方式是,使用Python引入的 with 结构来解决,如
 
defreadFile():
try:
withopen('mio','r')asf:
print''.join(f.readlines())
except:
print'erroroccurswhilereadingfile'
当文件打开失败时,异常自然会被 except 到;否则,在 with 块结束之后,打开的文件将自动关闭.
   除了打开文件,还有其它这样可以用于 with 的东西么?或者说,怎么自定义一个什么东西,让它能用于 with呢?
   直接回答后一个问题吧,秘密在于Python虚拟机在 with 块退出时会去寻找对象的 __exit__ 方法并调用它,把释放资源的动作放在这个 __exit__ 函数中就可以了;另外,对象还需要一个 __enter__ 函数,当进入 with块时,这个函数被调用,而它的返回值将作为 as 后引用的值.一个简单的例子是
 
classTest:
def__init__(self):
print'init'
def__enter__(self):
print'enter'
returnself
def__exit__(self,except_type,except_obj,tb):
printexcept_type
printexcept_obj
importtraceback
print''.join(traceback.format_tb(tb))
print'exit'
returnTrue
withTest()ast:
raiseValueError('kon!')
   执行这一段代码,输出将会是
 
init
enter
<type'exceptions.ValueError'>
kon!
File"test.py",line17,in<module>
raiseValueError('kon!')
exit
__exit__ 函数接受三个参数,分别是异常对象类型,异常对象和调用栈.如果 with 块正常退出,那么这些参数将都是 None.返回 True 表示发生的异常已被处理,不再继续向外抛出.
简单的介绍到此为止,详细的情况可以参考 PEP343 (这数字真不错,73).
下面介绍下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() ... withlock: elem=heapq.heappop(heap)
在此无论以何种方式离开with语句的代码块,锁都会被释放。
with语句的设计目的就是为了使得之前需要通过try...finally解决的清理资源问题变得简单、清晰,它的的用法是
 
withexpression[asvariable]: with-block
其中expression返回一个叫做「contextmanager」的对象,然后这个对象被赋给variable(如果有的话)。「contextmanager」对象有两个方法,分别是__enter__()和__exit__(),很明显一个在进入with-block时调用,一个离开with-block的时候调用。
这样的对象不需要自己去实现,在Python标准库里面很多API都是已经实现了这两个方法,最常见的一个例子就是读写文件的open语句。
 
withopen('1.txt',encoding='utf-8')asfp:
lines=fp.readlines()
无论是正常离开还是因为异常原因离开with语句块,打开的文件资源总是会释放。
接下去讨论一下with语句配合contextlib库的一些比较实用的方法,比如需要同时打开两个文件,一个读一个写,这个时候就可以这样写:
 
fromcontextlibimportnested
...
withnested(open('in.txt'),open('out.txt','w'))as(fp_in,fp_out):
...
这样就可以省掉两个with的语句的嵌套了,另外如果遇到一些还没有支持「contextmanager」的API呢?比如urllib.request.urlopen(),这个返回的对象因为不是「contextmanager」,结束的时候还需要自己去调用close方法。
类似这种API,contextlib提供了一个叫做closing方法,它会在离开with语句的时候,自动调用对象的close方法,因此urlopen也可以这样写:
 
fromcontextlibimportclosing
...
withclosing(urllib.request.urlopen('http://www.yahoo.com'))asf:
forlineinf:
sys.stdout.write(line)