在 `__init_subclass__` 中使用 `super()` 找不到父类的类方法

Using `super()` within `__init_subclass__` doesn't find parent's classmethod

我尝试从 __init_subclass__ 中访问父级的类方法,但这似乎不起作用。 假设以下示例代码:

class Foo:
    def __init_subclass__(cls):
        print('init', cls, cls.__mro__)
        super(cls).foo()

    @classmethod
    def foo(cls):
        print('foo')


class Bar(Foo):
    pass

这会产生以下异常:

AttributeError: 'super' object has no attribute 'foo'

然而 cls.__mro__ 显示 Foo 是其中的一部分:(<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>).

所以我不明白为什么 super(cls).foo() 不发送给 Foo.foo。有人可以解释一下吗?

一个普通的 super 对象(您通常通过调用 super(MyType, self)super()super(MyType, myobj) 获得的对象)跟踪创建它的类型和对象和。每当您在 super 上查找属性时,它都会跳过方法解析顺序中的 MyType,但如果它找到一个方法,它会将它绑定到那个 self 对象。

一个未绑定的 super 没有 self 对象。因此,super(cls) 跳过 MRO 中的 cls 找到方法 foo,然后将其绑定到……哎呀,它没有什么可调用的。

那么,什么东西可以称为 classmethod? class 本身,或它的子class,或 class 或子class 的实例。因此,其中任何一个都将作为 super 的第二个参数,最明显的是:

super(cls, cls)

这有点类似于静态方法(绑定 staticmethods 实际上没有绑定任何东西)和 classmethods(绑定 classmethods 绑定到 class 之间的区别一个实例),但它并不是那么简单。


如果您想知道为什么未绑定的 super 不起作用,您必须了解未绑定的 super 到底是什么。不幸的是,the docs中唯一的解释是:

If the second argument is omitted, the super object returned is unbound.

这是什么意思?好吧,你可以尝试从第一原则来解决它,将其与方法未绑定的含义相提并论(当然,现代 Python 中不存在未绑定方法),或者你可以读 the C source, or the original introduction to 2.2's class-type unification (including a pure-Python super clone).

一个super对象有一个__self__属性,就像一个方法对象。 super(cls) 缺少它的 __self__,就像 str.split 一样。1

您不能像使用未绑定方法那样使用未绑定 super 显式 (例如,str.split('123', '2')'123'.split('2'),但 super(cls).foo(cls)super(cls, cls).foo() 的工作方式不同)。但是您可以 隐含地使用它们 ,就像您一直使用未绑定方法一样,通常不会考虑它。

如果你不知道how methods work, the tl'dr is: when you evaluate myobj.mymeth, Python looks up mymeth, doesn't find it on myobj itself, but does find it on the type, so it checks whether it's a non-data descriptor,如果知道,调用它的__get__方法将它绑定到myobj

因此,未绑定方法2 是非数据描述符,其 __get__ 方法 returns 是绑定方法。未绑定的 @classmethod 类似,但是它们的 __get__ 忽略了对象和 returns 绑定到 class 的绑定方法。等等。

和未绑定的 supers 是非数据描述符,其 __get__ 方法 returns 绑定 super.


示例(感谢 wim 提出了我见过的最接近 unbound super 用途的东西):

class A:
    def f(self): print('A.f')
class B(A):
    def f(self): print('B.f')
b = B()
bs = super(B)
B.bs = bs
b.bs.f()

我们创建了一个未绑定的 super bs,将其粘贴在类型 B 上,然后 b.bs 是一个正常的绑定 super,所以 b.bs.fA.f,就像 super().f 会在 B 方法中一样。

你为什么要这样做?我不确定。我在 Python 中编写了各种荒谬的动态和反射代码(例如,用于其他解释器的透明代理),我不记得曾经需要一个未绑定的 super。但如果你需要它,它就在那里。


1.我在这里有点作弊。首先,未绑定方法在 Python 3 中不再存在——但函数的工作方式相同,因此 Python 在过去使用未绑定方法的地方使用它们。其次,str.split 作为一个 C 内置函数,即使在 2.x 中也不是一个正确的未绑定方法——但它无论如何都像一个,至少就我们在这里所关注的而言。

2。实际上是普通的函数。