据我了解,上下文管理器的__init__() 和__enter__() 方法每个只被调用一次,一个接一个,没有机会在其间执行任何其他代码。
而且你的理解是不正确的。 __init__ 在创建对象时调用,__enter__ 在使用 with 语句输入时调用,这是两个截然不同的东西。通常是在with初始化中直接调用构造函数,没有中间代码,但不一定是这样。
考虑这个例子:
class Foo:
def __init__(self):
print('__init__ called')
def __enter__(self):
print('__enter__ called')
return self
def __exit__(self, *a):
print('__exit__ called')
myobj = Foo()
print('\nabout to enter with 1')
with myobj:
print('in with 1')
print('\nabout to enter with 2')
with myobj:
print('in with 2')
myobj可以单独初始化,并在多个with块中输入:
输出:
__init__ called
about to enter with 1
__enter__ called
in with 1
__exit__ called
about to enter with 2
__enter__ called
in with 2
__exit__ called
此外,如果__init__ 和__enter__ 不分开,甚至无法使用以下内容:
def open_etc_file(name):
return open(os.path.join('/etc', name))
with open_etc_file('passwd'):
...
因为初始化(在open 内)明显与with 条目分开。
contextlib.manager 创建的管理器是单入的,但它们同样可以在with 块之外构建。举个例子:
from contextlib import contextmanager
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
您可以将其用作:
def heading(level=1):
return tag('h{}'.format(level))
my_heading = heading()
print('Below be my heading')
with my_heading:
print('Here be dragons')
输出:
Below be my heading
<h1>
Here be dragons
</h1>
但是,如果您尝试重用 my_heading(因此,tag),您将得到
RuntimeError: generator didn't yield