本文最后更新于 258 天前,其中的信息可能已经过时,如有错误请发送邮件到wuxianglongblog@163.com
上下文管理器
读写文件时,如果一个文件被打开,且未被正常关闭,可能会出现一些意想不到的结果。
Python提供了上下文管理器的机制来解决这个问题,它通常与关键字with一起使用。对于上面的例子,用with语句调用的方式为:
with <expression>:
<statements>
with open('my_file', 'w') as fp:
# do stuff with fp
data = fp.write("Hello world")
等价于:
fp = open('my_file', 'w')
try:
# do stuff with f
data = fp.write("Hello world")
finally:
fp.close()
处理文件,线程,数据库,网络编程等等资源的时候,经常需要使用上面这样的代码形式,以确保资源的正常使用和释放。
上下文管理器需要<expression>
中的结果能够支持.__enter__()
和.__exit__()
方法:
fp = open('my_file', 'w')
fp.__enter__
fp.__exit__
自定义上下文管理器
可以定义一个支持上述方法的自定义上下文管理器:
class TestManager(object):
def __enter__(self):
print("Entering")
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting")
with TestManager():
print("Hello")
Entering
Hello
Exiting
如果
with TestManager():
print(1 / 0)
Entering
Exiting
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Input In [8], in ()
1 with TestManager():
----> 2 print(1 / 0)
ZeroDivisionError: division by zero |
方法.enter()的返回值
在读文件的例子中,在<statements>
中使用文件对象时使用了as关键字的形式,将open()
函数返回的文件对象赋给了f。事实上,as关键字只是将上下文管理器.__enter__()
方法的返回值赋给了f,而文件对象的.__enter__()
方法的返回值刚好是它本身:
fp = open('my_file', 'w')
fp.__enter__() is fp
True
fp.close()
一个通常的做法是将.__enter__()
方法的返回值设为这个上下文管理器对象本身,也可以是其他值:
class TestManager(object):
def __enter__(self):
print("Entering")
return "Hello"
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting")
with TestManager() as f:
print(f)
Entering
Hello
Exiting
错误处理
__exit__()
方法接受的参数中有一些错误信息,如果没有错误,这些参数为None
,如果有错误,可以在这个方法里对一些错误进行处理:
class TestManager(object):
def __enter__(self):
print("Entering")
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting")
if exc_type is not None:
print(f"Exception: {exc_value}")
with TestManager() as f:
print(1 / 0)
Entering
Exiting
Exception: division by zero
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Input In [14], in ()
1 with TestManager() as f:
----> 2 print(1 / 0)
ZeroDivisionError: division by zero |
如果不想让异常继续抛出,只需要将.__exit__()
方法的返回值设为True
:
class TestManager(object):
def __enter__(self):
print("Entering")
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting")
if exc_type is not None:
print(f"Exception: {exc_value}")
return True
with TestManager() as f:
print(1 / 0)
Entering
Exiting
Exception: division by zero
清理临时文件:
%rm my_file