python元类继承问题

Posted

技术标签:

【中文标题】python元类继承问题【英文标题】:python metaclass inheritance issue 【发布时间】:2016-07-19 02:12:53 【问题描述】:

我有一个有点奇怪的元类问题。我正在使用元类动态创建一个继承自另一个超类的“兄弟”类,并将其分配为原始类的属性。下面是一个最小的设置:

class Meta(type):
def __new__(cls, name, parents, dct):
    sdct = dct.copy()
    dct['sibling'] = type(name+'Sibling', (Mom,), sdct)
    return super().__new__(cls, name, (Dad,), dct)

class Mom:
     def __init__(self):
         self.x = 3

class Dad:
     def __init__(self):
         self.x = 4

class Child(metaclass=Meta):
     def __init__(self):
         super().__init__()
         self.y = 1  # <<< added from feedback
print(Child().x) # 4
print(Child().sibling) # <class '__main__.Child'> | should be ChildSibling
print(Child().sibling().x) # should be 3 instead throws:
    # TypeError: super(type, obj): obj must be an instance or subtype of type
print(Child().sibling().y) # should print 4

上面的“兄弟”类的创建似乎出了点问题,但我不太确定是什么。例如,我知道这会起作用:

class ChildAbstract:
    def __init__(self):
        super().__init__()

ChildSibling = type('ChildSibling', (ChildAbstract, Mom), )
Child = type('Child', (ChildAbstract, Dad), 'sibling': ChildSibling)
print(Child().sibling().x) # 3

不过,我看不出这两种情况之间的区别。

【问题讨论】:

【参考方案1】:

传递给type的字典sdct包括__qualname__,根据这个PEP是reprstr 现在使用。

尝试添加

print(Child is Child.sibling)  # False
print(Child.sibling.__name__)  # "ChildSibling"

你会发现它确实是兄弟姐妹。

至于为什么sibling().x 抛出,同样的sdct 也已经包含Child.__init__,它最终是您动态创建的新类型ChildSibling__init__。在调用sibling() 期间,super() 调用将类解析为Child 并获得ChildSibling 的实例:

还要注意,除了零参数形式之外,super() 不限于使用内部方法。两个参数形式精确地指定参数并进行适当的引用。零参数形式仅适用于类定义,编译器填写必要的详细信息以正确检索正在定义的类,以及访问普通方法的当前实例。

https://docs.python.org/3/library/functions.html#super

通过将第一个参数作为实例传递给方法来访问当前实例。

super() -&gt; same as super(__class__, &lt;first argument&gt;)

在line 7210 of Object/typeobject.c 引发错误。

尝试删除__new__ 中的错误__init__

del sdct['__init__']

现在

print(Child().sibling().x)

将打印 3。

“通用”继承和元编程友好的解决方案 __init__ 是使用 super() 的 2 参数形式:

def __init__(self):
    super(self.__class__, self).__init__()

【讨论】:

有趣的一点,从字典中删除 init 是可行的,但这只是因为在我的玩具示例中,我在 Child.__init__ 中没有做任何事情,所以这并不能完全解决问题(更新代码来说明)。我会看看 qualname 不过 啊,关于 super 的注释说明了这一点 @Buck 您的第二个示例有效的原因是这两种动态类型的基础包括ChildAbstract,因此super() 很高兴。 所以super().__init__() -&gt; super(self.__class__, self).__init__()解决了这个问题,谢谢@Ilja @Buck 也将您的解决方案作为答案的一部分,因此更容易找到

以上是关于python元类继承问题的主要内容,如果未能解决你的问题,请参考以下文章

元类冲突、多重继承、实例为父

习惯从类型继承元类?

在 python3.x 中显式继承“类型”以实现元类

为啥元类不能访问由元类定义的类的子类继承的属性?

从 ABC 和 django.db.models.Model 继承会引发元类异常

unittest 模拟和多重继承:TypeError:元类冲突