атрибуты python-класса, по-видимому, не наследуются

в недавнем проекте я пытаюсь сделать что-то вроде этого (более сложный, но тот же результат):

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:UsersThorsteinDesktoptest.py", line 10, in <module>
    class B(A):
  File "C:UsersThorsteinDesktoptest.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 ответов


он не должен наследовать их. Метакласс получает атрибуты, определенные для создаваемого им класса, а не атрибуты его базовых классов. Весь смысл конструктора metaclass заключается в том, чтобы получить доступ к тому, что фактически задано в теле класса.

на самом деле, атрибуты базового класса действительно не "в" подклассе вообще. Это не так, как если бы они были "скопированы" в подкласс, когда вы его определяете, и на самом деле во время создания подкласс даже не " знает" какие атрибуты могут иметь его суперклассы. Скорее, когда вы открыть B.foo, Python сначала пытается найти атрибут на B, затем, если он не найдет его, смотрит на его суперклассы. Это происходит динамически каждый раз, когда вы пытаетесь прочитать такой атрибут. Механизм метакласса не должен иметь доступа к атрибутам суперкласса, потому что их действительно нет в подклассе.

возможно, связанная с этим проблема заключается в том, что ваше определение __new__ is неправильный. Аргументы метакласса __new__ являются cls, name, bases и attrs. Третий аргумент-это список базовых классов, а не" args " (независимо от того, что вы собираетесь делать). Четвертый аргумент-это список атрибутов, определенных в теле класса. Если вы хотите получить доступ к унаследованным атрибутам, вы можете получить их через базы.

в любом случае, что вы пытаетесь достичь с этой схемой? Возможно, есть лучший способ сделать это.


если вы используете __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

при создании это возвращает:

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

при создании B возвращает:

bar
{'__module__': '__main__'}

Примечание, и это то, что @BrenBarn говорил в своем ответ, что foo не attr, когда это. Вот почему код в ОП вопрос поднимает KeyError. Однако зовет self.foo ищет foo in B тогда, если он не может найти, он выглядит в своих основаниях, то есть A.

метакласс для обоих классов можно определить, посмотрев на ее класс:

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

дело в том, что метакласс __init__ метод вызывается при выделении и инициализации B, печать self.foo и attr на stdout также подтверждает, что Bмета-класс MetaA.