关于python:在__init_subclass__中使用”super()”找不到父类的classmethod

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

本问题已经有最佳答案,请猛点这里访问。

我试图从__init_subclass__中访问父类的classmethod,但这似乎不起作用。假设下面的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
class Foo:
    def __init_subclass__(cls):
        print('init', cls, cls.__mro__)
        super(cls).foo()

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


class Bar(Foo):
    pass

产生以下异常:

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

然而,cls.__mro__表明Foo是其中的一部分:(, , )

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


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

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

那么,你能称之为classmethodon的是什么?类本身或其子类,或该类或子类的实例。因此,其中任何一个都将成为super的第二个论点,最明显的一个论点是:

1
super(cls, cls)

这与静态方法(绑定staticmethods实际上绑定为Nothing)和classmethods之间的区别有点类似,但并不那么简单。

如果你想知道为什么一个未绑定的super不起作用,你必须理解一个未绑定的super到底是什么。不幸的是,文档中唯一的解释是:

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

这是什么意思?好吧,你可以试着从第一个原则出发,把它与解除绑定方法的含义(当然,在现代的python中,解除绑定方法不是一回事),或者你可以阅读c源代码,或者2.2类类型统一(包括纯python super克隆)的最初介绍。

super对象具有__self__属性,就像方法对象一样。而super(cls)则缺少了它的__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__忽略对象并返回绑定到类的绑定方法。等等。

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

示例(由于WIM提出了最接近使用未绑定super的方法,我已经看到了这一点):

1
2
3
4
5
6
7
8
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()

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

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

>1。我在这里有点作弊。首先,在Python3中,未绑定方法不再是一回事,但是函数的工作方式相同,所以Python在使用未绑定方法的地方使用它们。其次,str.split是C内置的,即使在2.x中,它也不是一种适当的未绑定方法,但它的行为无论如何都像一种方法,至少就我们这里所关注的那样。

>2。实际上是简单的旧函数。