有没有办法在类定义之后设置元类?

2024-05-14 04:11:05 发布

您现在位置:Python中文网/ 问答频道 /正文

为了设置类的元类,我们使用__metaclass__属性。元类是在定义类时使用的,因此在类定义之后显式地设置它没有任何效果。

当我试图显式地设置元类时,就会发生这种情况

>>> class MetaClass(type):
    def __new__(cls, name, bases, dct):
        dct["test_var"]=True
        return type.__new__(cls, name, bases, dct)
    def __init__(cls, name, bases, dct):
        super(MetaClass, cls).__init__(name, bases, dct)


>>> class A:
    __metaclass__=MetaClass


>>> A.test_var
True
>>> class B:
    pass

>>> B.__metaclass__=MetaClass
>>> B.test_var

Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    B.test_var
AttributeError: class B has no attribute 'test_var'

我能想到的最好的办法是重新定义整个类并以某种方式动态地添加__metaclass__属性。或者你知道在类定义之后设置元类的更好方法吗?


Tags: nametesttruenew属性定义vardef
3条回答

您可以在类创建后更改元类,就像更改对象的类一样,但是您会遇到很多问题。首先,初始元类需要与新元类的type不同,不会调用新元类的__init____new__(尽管您可以手动调用__init__或执行__init__作业的方法)。

更改元类最不麻烦的方法可能是从头重新创建类:

B = MetaClass(B.__name__, B.__bases__, B.__dict__)

但如果坚持动态更改元类,则首先需要使用临时自定义元类定义B:

class _TempMetaclass(type):
    pass

class B:
    __metaclass__ = _TempMetaclass # or: type('temp', (type, ), {})

然后您可以这样定义元类:

class MetaClass(type):
    def __init__(cls, *a, **kw):
        super(MetaClass, cls).__init__(*a, **kw)
        cls._actual_init(*a, **kw)
    def _actual_init(cls, *a, **kw):
        # actual initialization goes here

然后做如下事情:

B.__class__ = MetaClass
MetaClass._actual_init(B, B.__name__, B.__bases__, B.__dict__)

您还需要确保类的所有初始化都已完成_actual_init。您还可以向为您更改元类的元类添加classmethod

这两种解决方案都有一个小缺点,即B的基是有限的,它们需要与原始元类和新元类兼容,但我想这在您的情况下不是问题。

元类的目的不是修改属性访问,而是自定义类的创建。__metaclass__属性定义用于从类定义构造类型对象的可调用函数,默认为type()。因此,该属性仅在类创建时计算。很明显,以后再更改它是没有意义的,因为您不能为已经创建的类自定义类创建。有关详细信息,请参见Python Language Reference, 3.4.3 Customizing class creation

元类显然不是您想要做的事情的正确工具。如果您只想向一个已经存在的类添加一个新属性,为什么不直接分配它呢?

您可以认为元类描述了class语句的功能。这就是为什么以后设置元类是不可能的:类代码已经转换为类型对象,现在更改它已经太迟了。

您可以简单地将原始类型子类化以添加元类:

class C(B):
    __metaclass__=MetaClass

相关问题 更多 >