атрибуты 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
.