python - 类属性显然不是继承的

时间:2012-09-08 08:03:16

标签: python metaclass class-attributes

在最近的一个项目中,我尝试做这样的事情(更复杂,但结果相同):

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

基本上,想要在基色上运行一个函数来制作一个保存在类中的较暗的函数(类的多个实例将需要相同的颜色,但是会有更长的类层次结构)。我希望这会更有意义..

2 个答案:

答案 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.fooattr时调用元类stdout方法的事实也证明{{1}元类是B