python 3中类级别的__enter__和__exit__

时间:2015-02-04 20:05:39

标签: python python-3.x with-statement class-method

我尝试在类级别上运行的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__方法?

2 个答案:

答案 0 :(得分:12)

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

现在Spamtype的实例,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

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

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

with Spam():
    pass

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

spam = Spam()
with spam:
    pass

答案 1 :(得分:0)

似乎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)的主要想法也适用于它