在`__init_subclass__`中使用`super()`找不到parent的classmethod

a_g*_*est 8 python inheritance class-method python-3.6

我尝试从内部访问父类的类方法,__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
Run Code Online (Sandbox Code Playgroud)

这会产生以下异常:

AttributeError: 'super' object has no attribute 'foo'
Run Code Online (Sandbox Code Playgroud)

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

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

aba*_*ert 11

普通super对象(您通常从调用super(MyType, self)super()或获得的对象super(MyType, myobj))跟踪它所创建的类型和对象.无论何时查找属性super,它都会MyType以方法解析顺序跳过,但如果找到方法,则会将其绑定到该self对象.

未绑定super 没有self对象.所以,super(cls)跳过clsMRO找到方法foo,然后将它绑定到... oops,它没有任何东西可以调用它.

那么,你可以称之为classmethod什么?类本身或其子类,或该类或子类的实例.所以,其中任何一个都将作为第二个参数super,最明显的一个是:

super(cls, cls)
Run Code Online (Sandbox Code Playgroud)

这有点类似于staticmethods之间的区别(bound staticmethods实际上绑定到什么都没有)和classmethods(bound classmethods绑定到类而不是实例),但它并不那么简单.


如果您想知道为什么未绑定super不起作用,您必须了解未绑定的super内容.不幸的是,文档中唯一的解释是:

如果省略第二个参数,则返回的超级对象是未绑定的.

这是什么意思?好吧,您可以尝试从第一原则开始,将其与未绑定方法的含义并行(当然,未绑定的方法不是现代Python中的东西),或者您可以阅读C源代码,或2.2的类型统一super的原始介绍(包括纯Python 克隆).

super对象具有__self__属性,就像一个方法的对象.并且正如它一样super(cls)缺少它.1__self__str.split

您不能以未绑定方法的方式super 显式使用未绑定(例如,str.split('123', '2')相同'123'.split('2')super(cls).foo(cls)不相同super(cls, cls).foo()).但是你可以隐式使用它们,就像你对未绑定方法一样,而不用考虑它.

如果您不知道方法如何工作的,那么tl'dr是:当您评估时myobj.mymeth,Python查找mymeth,找不到它myobj,但确实在类型上找到它,因此它检查它是否是非数据描述符,如果是这样,则调用其__get__方法将其绑定到myobj.

因此,未绑定方法2是非数据描述符,其__get__方法返回绑定方法.未绑定的@classmethods类似,但它们__get__忽略该对象并返回绑定到该类的绑定方法.等等.

未绑定的supers是非数据描述符,其__get__方法返回一个绑定super.


示例(wim为我提供了最接近未绑定使用的东西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()
Run Code Online (Sandbox Code Playgroud)

我们创建了一个绑定的超级bs,把它贴类型B,然后b.bs是一个正常的绑定超好,所以b.bs.f就是A.f,就像super().f本来是一个内部的B方法.

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


我在这里作弊.首先,未绑定的方法在Python 3中不再是一个东西 - 但函数的工作方式相同,因此Python使用它们来使用未绑定的方法.其次,str.split作为一个C内置,即使在2.x中也不是一种不受约束的方法 - 但它无论如何都会像一个人一样,至少就我们所关注的而言.

2.实际上是普通的功能.