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

Utk*_*glu 13 python metaclass

为了设置类的元类,我们使用该__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'
Run Code Online (Sandbox Code Playgroud)

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

Ros*_*ron 11

您可以在创建类之后更改元类,就像更改对象的类一样,但是您会遇到很多问题.对于初学者来说,初始元类需要不同type,不会调用新的元类__init____new__(虽然你可以手动调用__init__或执行 __init__工作的方法).

改变元类的最麻烦的方法可能是从头开始重新创建类:

B = MetaClass(B.__name__, B.__bases__, B.__dict__)
Run Code Online (Sandbox Code Playgroud)

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

class _TempMetaclass(type):
    pass

class B:
    __metaclass__ = _TempMetaclass # or: type('temp', (type, ), {})
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样定义元类:

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
Run Code Online (Sandbox Code Playgroud)

然后做一些像:

B.__class__ = MetaClass
MetaClass._actual_init(B, B.__name__, B.__bases__, B.__dict__)
Run Code Online (Sandbox Code Playgroud)

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

两种解决方案都存在B的基数有限的缺点 - 它们需要兼容原始和新的元类,但我想这不是你的问题.

  • 谢谢你的回答,`B = MetaClass(B .__ name __,B .__ bases __,B .__ dict __)`正是我所需要的. (2认同)

lun*_*orn 5

元类的目的不是修改属性访问,而是自定义类创建.该__metaclass__属性定义用于从类定义构造类型对象的可调用对象,默认为type().因此,仅在创建类时评估此属性.在任何以后的点上改变它显然没有任何意义,因为你不能为已经创建的类自定义类创建.有关详细信息,请参阅Python语言参考,3.4.3自定义类创建.

元数据不是你明显想要做的正确的工具.如果您只是想为现有的类添加新属性,为什么不直接分配它?