__在python 3的类级别上输入和退出

__在python 3的类级别上输入和退出,python,python-3.x,with-statement,class-method,Python,Python 3.x,With Statement,Class Method,我尝试使用-statement方法\uuuuu enter\uuuuu和\uuuu exit\uuuuu在类级别上运行,但未成功: class Spam(): @classmethod def __enter__(cls): return cls @classmethod def __exit__(cls, typ, value, tb): cls.cleanup_stuff() with Spam: pass 但

我尝试使用-statement方法
\uuuuu enter\uuuuu
\uuuu exit\uuuuu
在类级别上运行,但未成功:

class Spam():

    @classmethod
    def __enter__(cls):
        return cls

    @classmethod
    def __exit__(cls, typ, value, tb):
        cls.cleanup_stuff()


with Spam:
    pass
但是,这将导致
属性错误

Traceback (most recent call last):
  File "./test.py", line 15, in <module>
    with Spam:
AttributeError: __exit__
回溯(最近一次呼叫最后一次):
文件“/test.py”,第15行,在
垃圾邮件:
AttributeError:\uuu退出__

是否可以在类级别上使用
\uuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuu
方法?

似乎CPython不会调用像
实例那样的绑定方法。\uuuuuuuuuuuuuuuuuuuuuuuu退出
,它会查找实例类型,执行类似于
类型(实例)的操作。而且由于
type(Spam)
是一个特殊的
type
对象(而不是
Spam
本身),因此它不包含
\uuuuuuuu退出
方法

我试图用元类解决这个问题,但没有成功<代码>\uuu getattr\uuu
也不起作用

请看这里:

  • Py_类型与
    类型(自身)
  • _PyType\u LookupId遍历
    类型(self)。\uuuuu dict\uuuuuuu
    (无
    \uuuuuuu getattr\uuuuuu
    在此处调用)

Python2实现是不同的,但获取
类型(self)
的主要思想也适用于它

\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu和
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

现在,
Spam
type
type(Spam)的一个实例。\uuuuuuu输入\uuuuuuuuuu和
type(Spam)。\uuuuu退出\uuuuuuuuu
不存在。因此,您会得到一个属性错误

为了实现这一点,需要在要使用的类的元类上声明这些方法。例如:

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)。\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

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

with Spam():
    pass
或者,如果以后要重用同一实例:

spam = Spam()
with spam:
    pass

我认为@classmethod应该不再需要了。有和没有classmethod时,哪个类将作为
cls
传递是有区别的,有了它将是
Spam
,没有它将是
egs
,所以虽然技术上不需要它,它不一样。+1,但请删除
@classmethod
装饰符。元类方法的第一个参数已经是类。用
@classmethod
装饰意味着你得到的是元类而不是类,这很少是你想要的。谢谢。这正是我要找的。@Kevin好的,把它删除了,你和@nneonneo是对的,把它放在元类上没有什么意义。这样,
cleanup\u stuff
方法也可以在
Eggs
上声明,而不仅仅是在
Spam
上声明……你为什么还要尝试这样做?我的建议是按预期使用with语句。我打算用类级清理功能扩展peewee.Model类。