在最近的一个项目中,我尝试做这样的事情(更复杂,但结果相同):
class MetaA(type):
def __new__(cls, name, args, kwargs):
print kwargs["foo"]
return type.__new__(cls, name, args, kwargs)
class A(object):
__metaclass__ = MetaA
foo = "bar"
class B(A):
pass
我明白了:
bar
Traceback (most recent call last):
File "C:\Users\Thorstein\Desktop\test.py", line 10, in <module>
class B(A):
File "C:\Users\Thorstein\Desktop\test.py", line 3, in __new__
print kwargs["foo"]
KeyError: 'foo'
类属性是否未继承?如果是这样,在与上述相似的框架中是否有可能的解决方法?
编辑: 使用程序中的实际(简化)示例可能更容易看出我的意思..
class GameObjectMeta(type):
def __new__(cls, name, bases, attrs):
attrs["dark_color"] = darken(*attrs["color"])
return type.__new__(cls, name, bases, attrs)
class GameObject(object):
__metaclass__ = GameObjectMeta
color = (255,255,255) # RGB tuple
class Monster(GameObject):
pass
基本上,想要在基色上运行一个函数来制作一个保存在类中的较暗的函数(类的多个实例将需要相同的颜色,但是会有更长的类层次结构)。我希望这会更有意义..
答案 0 :(得分:3)
不应该继承那些。元类接收在它实例化的类上定义的属性,而不是其基类的属性。元类构造函数的重点是访问类体中实际给出的内容。
实际上,基类的属性实际上根本不在“子”类中。当你定义子类时,并不是将它们“复制”到子类中,实际上在创建时,子类甚至“不知道”它的超类可能具有哪些属性。相反,当您访问 B.foo
时,Python首先尝试在B上找到属性,然后如果找不到它,则查看其超类。每次尝试读取此类属性时都会动态发生这种情况。元类机制不应该访问超类的属性,因为那些子类实际上不存在。
可能相关的问题是您对__new__
的定义不正确。元类__new__
的参数是cls,name,bases和attrs。第三个参数是基类列表,而不是“args”(无论你想要的是什么)。第四个参数是类体中定义的属性列表。如果你想访问继承的属性,你可以通过基础获取它们。
无论如何,你想用这个方案完成什么?可能有更好的方法。
答案 1 :(得分:0)
如果您使用__init__
而不是__new__
,那么您可以继承基类属性:
class MetaA(type):
def __init__(self, name, bases, attr):
print self.foo # don't use attr because foo only exists for A not B
print attr # here is the proof that class creation does not follow base class attributes
super(MetaA, self).__init__(name, base, attr) # this line calls type(), but has no effect
class A(object):
__metaclass__ = MetaA
foo = 'bar'
class B(A):
pass
创建A时返回:
bar
{'__module__': '__main__', 'foo': 'bar', '__metaclass__': <class '__main__.MetaA'>}
创建B时返回:
bar
{'__module__': '__main__'}
注意,这就是@BrenBarn在他的answer中所说的,foo
在创建attr
时不在B
。这就是OP question中的代码引发KeyError的原因。但是,调用self.foo
会在foo
中查找B
,如果找不到,则会在其基础中查找IE A
。
这两个类的元类可以通过查看它的类来确定:
>>> print A.__class__
<class '__main__.MetaA'>
>>> print A.__class__
<class '__main__.MetaA'>
在__init__
中分配和初始化B
,打印self.foo
和attr
时调用元类stdout
方法的事实也证明{{1}元类是B
。