我没有成功地尝试在类级别运行神奇的
with
语句方法__enter__
和__exit__
:
class Spam():
@classmethod
def __enter__(cls):
return cls
@classmethod
def __exit__(cls, typ, value, tb):
cls.cleanup_stuff()
with Spam:
pass
但是,这会导致
AttributeError
:
Traceback (most recent call last):
File "./test.py", line 15, in <module>
with Spam:
AttributeError: __exit__
是否可以在类级别使用
__enter__
和 __exit__
方法?
__enter__
和 __exit__
是特殊方法,因此只有在 在对象类型上定义时才能正常工作,而不是在其实例字典中定义。
现在
Spam
是 type
的实例,type(Spam).__enter__
和 type(Spam).__exit__
不存在。因此你会得到一个属性错误。
要实现此目的,需要在要使用的类的元类上声明这些方法。示例:
class Spam(type):
def __enter__(cls):
print('enter')
return cls
def __exit__(cls, typ, value, tb):
print('exit')
class Eggs(metaclass=Spam):
pass
with Eggs:
pass
现在
Eggs
是 Spam
的实例(type(Eggs)
== Spam
,因此 type(Eggs).__enter__
和 type(Eggs).__exit__
确实存在)。
然而,定义一个元类只是为了使用它的实例作为上下文管理器似乎有点过分了。从您的示例开始,更直接的解决方案就是使用
with Spam():
pass
或者如果您想稍后重用同一实例:
spam = Spam()
with spam:
pass
CPython 似乎不会调用像
instance.__exit__
这样的绑定方法,它会查找实例类型,执行类似 type(instance).__dict__['__exit__']
的操作而不是调用它。并且由于 type(Spam)
是一个特殊的 type
对象(不是 Spam
本身),因此它不包含 __exit__
方法。
我尝试使用元类来解决这个问题,但没有成功。
__getattr__
也不起作用。
type(self)
type(self).__dict__
(这里没有__getattr__
调用)Python 2 实现有所不同,但关于获取
type(self)
的主要思想也适用于它
@classmethod
和 @contextlib.contextmanager
来创建充当上下文管理器的类方法:
from __future__ import annotations
from contextlib import contextmanager
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Iterator, Self
class Spam:
@classmethod
@contextmanager
def context(cls) -> Iterator[type[Self]]:
try:
# Setup here
yield cls
finally:
cls.cleanup_stuff()
with Spam.context():
pass
请注意,装饰器的顺序很重要。
参考: