开域资源管理
在开始介绍 IO 操作之前,先介绍一个 Python 提供的语句:with(开域语句)。在之前的异常处理一节,我们提到了可以使用try...finally来进行出现异常之后的资源清理操作。但是每次这样书写都十分繁琐,例如:
try:
f = open('/path/file', 'r')
print(f.read())
finally:
if f:
f.close()
所以 Python 就提供了with语句,它与try...finally语句的效果是一致的,会自动调用其打开的资源的关闭方法。例如:
with open('/path/file', 'r') as f:
print(f.read())
当然并不是只有open()函数返回的对象才可以使用with语句,任何对象只要正确的实现了上下文管理,就可以使用于with语句。上下文管理是通过__enter__和__exit__两个方法实现的。例如:
class AutoCloseable:
def __init__(self):
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def query(self):
pass
def close(self):
pass
在 Python 3.10 之前的版本中。每个with语句只能打开一个资源,如果需要打开多个资源,就需要用多个with语句嵌套使用。在 Python 3.10 版本中,with语句被改进了,现在可以在with语句中用圆括号()和逗号同时打开多个资源,例如with (Context1() as example, Context2()):。其中圆括号中的内容可以连续书写,也可以连续多行书写。
或者还可以使用contextlib中的contextmanager来实现。例如:
from contextlib import contextmanager
class AutoCloseable:
def __init__(self):
pass
def query(self):
pass
@contextmanager
def create_autocloseable():
a = Autocloseable()
yield a
with create_autocloseable() as a:
a.query()
如果希望在某段代码前后自动执行特定代码,也可以使用@contextmanager实现。例如:
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
with tag("h1"):
print("hello")
上述代码的执行顺序为:
with首先执行yield之前的语句。yield执行with内部的所有语句。- 执行
yield之后的所有语句。
因此,@contextmanager可以通过编写生成器来简化上下文管理