9

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

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

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

4

2 回答 2

4

它不应该继承那些。元类接收定义在它实例化的类上的属性,而不是它的基类的属性。元类构造函数的重点是访问类主体中实际给出的内容。

事实上,基类的属性根本不在子类中。当您定义它时,它们并没有被“复制”到子类中,实际上在创建时子类甚至不“知道”它的超类可能具有的属性。相反,当您访问 B.foo时,Python 首先尝试查找 B 上的属性,然后如果找不到,则查找其超类。每次您尝试读取此类属性时,都会动态发生这种情况。元类机制不应该访问超类的属性,因为子类中确实不存在这些属性。

一个可能相关的问题是您的定义__new__不正确。元类的参数__new__是 cls、name、bases 和 attrs。第三个参数是基类列表,而不是“args”(无论您打算如何)。第四个参数是类主体中定义的属性列表。如果您想访问继承的属性,您可以通过基础获得它们。

无论如何,你想用这个方案来完成什么?可能有更好的方法来做到这一点。

于 2012-09-08T08:14:52.140 回答
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 在他的回答中所说的,foo不是在attr何时B创建的。这就是OP 问题中的代码引发 KeyError 的原因。但是,如果找不到,则调用self.foofoo在其基础 IE 中查找。BA

这两个类的元类可以通过查看其类来确定:

>>> print A.__class__
<class '__main__.MetaA'>
>>> print A.__class__
<class '__main__.MetaA'>

在分配和初始化、打印和输入__init__时调用元类方法的事实也证明了元类是。Bself.fooattrstdoutBMetaA

于 2013-11-20T19:09:04.587 回答