python 元类继承问题

python metaclass inheritance issue

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

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

上面创建 'sibling' class 时似乎出了点问题,但我不太确定是什么。例如,我知道这会起作用:

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

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

虽然我看不出这两种情况之间的区别。

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

尝试添加

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

你会发现它真的是兄弟。

至于 sibling().x 抛出的原因,同样的 sdct 也已经包含 Child.__init__,它最终成为动态的 __init__创建了新类型 ChildSibling。在调用 sibling() 期间,super() 调用将 class 解析为 Child 并获得 ChildSibling:

的实例

Also note that, aside from the zero argument form, super() is not limited to use inside methods. The two argument form specifies the arguments exactly and makes the appropriate references. The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.

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

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

super() -> same as super(__class__, <first argument>)

错误出现在 line 7210 of Object/typeobject.c

尝试删除 __new__ 中错误的 __init__

del sdct['__init__']

现在

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

将打印 3。

"generic" 继承和元编程更友好 __init__ 的解决方案是使用 super():

的 2 参数形式
def __init__(self):
    super(self.__class__, self).__init__()