为了设置类的元类,我们使用__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__
属性。或者你知道在类定义之后设置元类的更好方法吗?
答案 0 :(得分:11)
您可以在创建类之后更改元类,就像更改对象的类一样,但是您会遇到很多问题。对于初学者,初始元类需要与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的基数有限的缺点 - 它们需要兼容原始和新的元类,但我想这不是你的问题。
答案 1 :(得分:5)
元类的目的不是修改属性访问,而是自定义类创建。 __metaclass__
属性定义用于从类定义构造类型对象的可调用对象,默认为type()
。因此,仅在类创建时评估此属性。在任何以后的点上改变它显然没有任何意义,因为你不能为已经创建的类自定义类创建。有关详细信息,请参阅Python Language Reference, 3.4.3 Customizing class creation。
元数据不是你明显想要做的正确的工具。如果您只想将新属性添加到现有类中,为什么不直接分配它?
答案 2 :(得分:2)
您可以认为元类描述了class
语句的作用。这就是为什么以后设置元类是不可能的:类代码已经转换为类型对象,改变它为时已晚。
您可以简单地将原始类型子类化为添加元类:
class C(B):
__metaclass__=MetaClass
答案 3 :(得分:-3)
你为什么需要这个?我不相信这是可能的,但是如果你能解释用例,我可能会提出另一种选择。
按照重新定义整个班级的方式,你可以做的是:
import types
NewClass = types.ClassType('NewClass', (object, ), {'__metaclass__':MetaClass})
然后只需复制所有旧方法:
for item in dir(OldClass):
setattr(NewClass, item, getattr(OldClass, item))
答案 4 :(得分:-6)
我现在无法测试它(我现在无法在我的机器上访问Python :-(),但除非元类是可以改变的,否则你可以尝试类似的东西这样:
>>> class B:
pass
>>> setattr(B, "__metaclass__", MetaClass)
同样,我现在无法测试它,所以如果这是一个无效的答案我只是道歉,只是想帮助。 :)