__enter__ 和 __exit__ 在 python 级别 class 3

__enter__ and __exit__ on class level in python 3

我试图在 class 级别上获得神奇的 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__

是否可以在 class 级别上使用 __enter____exit__ 方法?

似乎 CPython 没有像 instance.__exit__ 那样调用绑定方法,它寻找实例类型,做一些像 type(instance).__dict__['__exit__'] 的事情而不是调用它。由于 type(Spam) 是一个特殊的 type 对象(不是 Spam 本身),它不包含 __exit__ 方法。

我尝试使用元类来解决这个问题,但没有成功。 __getattr__ 也不行。

看这里:https://github.com/python/cpython/blob/2545fdbd4b4a6a77b132fccf816578f59b609be5/Objects/typeobject.c#L1362

  • Py_TYPE 类似于 type(self)
  • _PyType_LookupId走过type(self).__dict__(这里没有__getattr__调用)

Python 2 实现不同,但关于获取 type(self) 的主要思想也适用于它

__enter____exit__ 是特殊方法,因此只有在 defined on a object's type 时才能正常工作,而不是在它的实例字典中。

现在Spamtype的实例,type(Spam).__enter__type(Spam).__exit__不存在。因此你会得到一个属性错误。

要实现此功能,需要在您要使用的 class 的元 class 上声明这些方法。示例:

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

现在 EggsSpam 的实例(type(Eggs) == Spam,因此 type(Eggs).__enter__type(Eggs).__exit__ 确实存在) .

然而定义一个元class只是为了将它的一个实例用作上下文管理器似乎有点过头了。从您的示例开始的更直接的解决方案是仅使用

with Spam():
    pass

或者如果您想稍后重用同一个实例:

spam = Spam()
with spam:
    pass