Pregunta

In a recent project I try to do something like this (more complex, but the same result):

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

I get this:

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'

Are class attributes not inherited? If so, is there any workaround possible in a similar framework to the above?

EDIT: Might be easier to see what I mean using an actual (simplified) example from the program..

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

Basically, want to run a function on the base color to make a darker one that's saved in the class (multiple instances of the class will want the same color, but there will be a longer class hierarchy). I hope this'll make more sense..

¿Fue útil?

Solución

It's not supposed to inherit those. The metaclass receives the attributes defined on the class it is instantiating, not those of its base classes. The whole point of the metaclass constructor is to get access to what is actually given in the class body.

In fact, the attributes of the base class really aren't "in" the subclass at all. It's not as if they are "copied" to the subclass when you define it, and in fact at creation time the subclass doesn't even "know" what attributes its superclasses might have. Rather, when you access B.foo, Python first tries to find the attribute on B, then if it doesn't find it, looks on its superclasses. This happens dynamically every time you try to read such an attribute. The metaclass mechanism isn't supposed to have access to attributes of the superclass, because those really aren't there on the subclass.

A perhaps related problem is that your definition of __new__ is incorrect. The arguments to the metaclass __new__ are cls, name, bases, and attrs. The third argument is the list of base classes, not "args" (whatever you intend that to be). The fourth argument is the list of attributes define in the class body. If you want access to inherited attributes you can get at them via the bases.

Anyway, what are you trying to accomplish with this scheme? There's probably a better way to do it.

Otros consejos

If you use the __init__ instead of __new__ then you can inherit the base class attributes:

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

when A is created returns:

bar
{'__module__': '__main__', 'foo': 'bar', '__metaclass__': <class '__main__.MetaA'>}

when B is created returns:

bar
{'__module__': '__main__'}

Note, and this is what @BrenBarn was saying in his answer, that foo is not in attr when B is created. That's why the code in OP question raises KeyError. However calling self.foo looks for foo in B then if it can't find it looks in its bases, IE A.

The metaclass for both classes can be determined by looking at its class:

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

The fact that the metaclass __init__ method is called when allocating and initializing B, printing self.foo and attr in stdout also proves that B's meta class is MetaA.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top