with是对try…expect…finally语法的一种简化。
1 try except
python中try/except/else/finally语句的完整格式如下所示:
try:
Normal execution block
except A:
Exception A handle
except B:
Exception B handle
except:
Other exception handle
else:
if no exception,get here
finally:
print("finally")
说明:
正常执行的程序在try下面的Normal execution block执行块中执行,在执行过程中如果发生了异常,则中断当前在Normal execution block中的执行跳转到对应的异常处理块中开始执行;
python从第一个except X处开始查找,如果找到了对应的exception类型则进入其提供的exception handle中进行处理,如果没有找到则直接进入except块处进行处理。except块是可选项,如果没有提供,该exception将会被提交给python进行默认处理,处理方式则是终止应用程序并打印提示信息;
如果在Normal execution block执行块中执行过程中没有发生任何异常,则在执行完Normal execution block后会进入else执行块中(如果存在的话)执行。
无论是否发生了异常,只要提供了finally语句,以上try/except/else/finally代码块执行的最后一步总是执行finally所对应的代码块。
2 with as
with后面跟的是一个对象,这个对象必须要有一个__enter__()方法,一个__exit__()方法。
代码执行时,首先对with后面的语句进行求值,得到一个对象,该对象的__enter__()方法会被调用,__enter__()方法的返回值会被赋给as 后面的变量。with as后面的语句块执行完毕或者抛出异常后,会调用前面返回对象的__exit__()方法。
使用python with as的用法中的两个例子
class Sample:
def __enter__(self):
print ("In __enter__()")
return "Foo"
def __exit__(self, type, value, trace):
print ("In __exit__()")
def get_sample():
return Sample()
with get_sample() as sample:
print( "sample:", sample)
执行get_sample() 返回一个Sample对象;
执行该Sample对象的__enter__()方法;
输出In __enter__(),返回“Foo”赋值给sample;
执行print( "sample:", sample),输出sample: Foo;
执行该Sample对象的__exit__()方法,输出In __exit__()
结果如下
In __enter__()
sample: Foo
In __exit__()
__exit__方法有三个参数- val, type 和 trace,使用它们可以打印出异常信息。
class Sample:
def __enter__(self):
return self
def __exit__(self, type, value, trace):
print ("type:", type)
print ( "value:", value)
print ("trace:", trace)
def do_something(self):
bar = 1/0
return bar + 10
with Sample() as sample:
sample.do_something()
输出结果
type: <class 'ZeroDivisionError'>
value: division by zero
trace: <traceback object at 0x02D17AA8>
Traceback (most recent call last):
File "D:/Study/code/python/exampleOfWith.py", line 30, in <module>
sample.do_something()
File "D:/Study/code/python/exampleOfWith.py", line 26, in do_something
bar = 1/0
ZeroDivisionError: division by zero
在with后面的代码块抛出任何异常时,__exit__()方法被执行。正如例子所示,异常抛出时,与之关联的type,value和stack trace传给__exit__()方法,因此抛出的ZeroDivisionError异常被打印出来了。开发库时,清理资源,关闭文件等等操作,都可以放在__exit__方法当中。
3 使用with as进行文件处理
在文件处理中,获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。执行的语句如下
fileIn = open("D:/xiaoaojianghu_jinyong.txt",encoding='utf-8')
data = fileIn.read()
fileIn.close()
使用第一节的try语句进行异常处理
fileIn = open("D:/xiaoaojianghu_jinyong.txt",encoding='utf-8')
try:
data = fileIn.read()
finally:
fileIn.close()
使用with as,更加方便。
with open("D:/xiaoaojianghu_jinyong.txt",encoding='utf-8') as fileIn:
data = fileIn.read()
查看open的定义(在_pyio.py)
@property
def closed(self):
"""closed: bool. True iff the file has been closed.
For backwards compatibility, this is a property, not a predicate.
"""
return self.__closed
def _checkClosed(self, msg=None):
"""Internal: raise a ValueError if file is closed
"""
if self.closed:
raise ValueError("I/O operation on closed file."
if msg is None else msg)
### Context manager ###
def __enter__(self): # That's a forward reference
"""Context management protocol. Returns self (an instance of IOBase)."""
self._checkClosed()
return self
def __exit__(self, *args):
"""Context management protocol. Calls close()"""
self.close()
__enter__()会检查file是否是close
__exit__会调用close()。在文件处理完毕后或出现异常时,文件也可以被正常关闭。