在@contextmanager vs"上显式调用__enter__与"之间的原因不明。声明

时间:2018-04-05 18:51:27

标签: python python-2.7 with-statement contextmanager

我有一个上下文管理器类型(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__被调用?

1 个答案:

答案 0 :(得分:3)

您放弃了上下文管理器,因此生成器对象符合回收条件。生成器的__del__ *会自动调用生成器上的close,这会在最后GeneratorExit点引发yield,结束with Connection() as c阻止并触发c.__exit__

*从技术上讲,在Python 2上,这实际上发生在比__del__更低级别的清理例程中。