【问题标题】:python - Class attributes apparently not inheritedpython - 类属性显然没有继承
【发布时间】:2012-09-01 23:39:52
【问题描述】:

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

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

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

【问题讨论】:

    标签: python metaclass class-attributes


    【解决方案1】:

    如果您使用__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 中所说的,当B 被创建时foo 不在attr 中。这就是OP question 中的代码引发 KeyError 的原因。但是调用self.fooB 中查找foo,然后如果找不到它会在其基础中查找,即IE A

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

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

    在分配和初始化B时调用元类__init__方法,在stdout中打印self.fooattr也证明B的元类是MetaA

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

      • 谢谢,我好像对__new__ 参数感到困惑。我想我可以使用它找到解决问题的方法。我想要完成的是访问一个属性来创建第二个(仅使用上面的示例,执行kwargs["cheese"] = kwargs["foo"]+"bar")。基本上提供了修改子类中的“foo”属性的选项,并且“cheese”总是以相同的方式使用子类值或默认的父类值。我使用 python 类定义代替数据文件(django 风格),所以我希望类尽可能容易编写。
      • 可能阅读this 文章可能有助于理解当您说“查看它的超类”时,这通常不是我们期望的那样。即使这仅在使用多重继承时才会发生。
      • 在根据您的回复进行了一番摸索之后,我想通了(尽管我很愚蠢)。我检查子类中是否存在color 属性,如果没有,我不担心。然后它将从父级继承colordark_color。感谢您的帮助!
      • 这确实清除了传递给元类 __new____init__ 方法的属性字典,并且在导入模块并通过调用元类为类分配内存时不考虑基类' __new__ 方法(然后可能在元类的 __init__ 方法期间初始化这些类对象)。这个答案还阐明了如何在运行时动态地从子类调用基类属性,而不是在导入模块时在类创建期间调用。这篇文章将受益于支持这些重要事实的参考。
      猜你喜欢
      • 2017-02-09
      • 1970-01-01
      • 1970-01-01
      • 2012-05-03
      • 2013-09-02
      • 2022-12-18
      • 1970-01-01
      • 2012-07-25
      相关资源
      最近更新 更多