我有一个上下文管理器类型(Connection
)和一个@contextmanager
修饰函数,可以在with
语句中生成该类型。
如果我在装饰函数上显式调用__enter__
,则会在__exit__
之前调用Connection
。
这是代码:
from __future__ import print_function
from contextlib import contextmanager
class Connection(object):
def __init__(self):
self.closed = False
def __enter__(self):
print('Connection.__enter__')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('Connection.__exit__')
self.closed = True
return False
def __repr__(self):
return "{}(closed={})".format(self.__class__.__name__, self.closed)
@contextmanager
def connect():
with Connection() as c:
print('connect yielding connection')
yield c
print('connect yielded connection')
def context():
print("connect explicit call to __enter__")
c = connect().__enter__()
print("got connection via __enter__", c)
print()
print("connect implicit call to __enter__ via with")
with connect() as c:
print("got connection in 'with'", c)
print("connection after 'with'", c)
print()
print("Connection explicit call to __enter__")
c = Connection().__enter__()
print("got connection via __enter__", c)
print()
print("Connection implicit call to __enter__ via with")
with Connection() as c:
print("got connection in with", c)
print("connection after 'with'", c)
if __name__ == "__main__":
context()
运行时输出:
connect explicit call to __enter__
Connection.__enter__
connect yielding connection
Connection.__exit__
got connection via __enter__ Connection(closed=True)
connect implicit call to __enter__ via with
Connection.__enter__
connect yielding connection
got connection in 'with' Connection(closed=False)
connect yielded connection
Connection.__exit__
connection after 'with' Connection(closed=True)
Connection explicit call to __enter__
Connection.__enter__
got connection via __enter__ Connection(closed=False)
Connection implicit call to __enter__ via with
Connection.__enter__
got connection in with Connection(closed=False)
Connection.__exit__
connection after 'with' Connection(closed=True)
比较以&#34开头的序列;将显式调用连接到__enter __" to"通过"。
将隐式调用连接到__enter__当我在__enter__
修饰函数上显式调用@contextmanager
时,为什么在连接返回给我之前调用Connection.__exit__
?为什么"连接产生连接"从未打印过 - 所以这意味着它仍然处于yield语句并且还没有离开with
块 - 那么为什么__exit__
被调用?
答案 0 :(得分:3)
您放弃了上下文管理器,因此生成器对象符合回收条件。生成器的__del__
*会自动调用生成器上的close
,这会在最后GeneratorExit
点引发yield
,结束with Connection() as c
阻止并触发c.__exit__
。
*从技术上讲,在Python 2上,这实际上发生在比__del__
更低级别的清理例程中。