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

时间:2018-04-18 00:07:03

标签: python inheritance python-3.6 class-method

我尝试从__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。有人可以解释一下吗?

1 个答案:

答案 0 :(得分:10)

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

未绑定的super 没有self个对象。因此,super(cls)跳过MRO中的cls来查找方法foo,然后将其绑定到... oops,它没有任何内容可以调用它。

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

super(cls, cls)

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

如果您想知道未绑定的super无效的原因,您必须了解未绑定的super究竟是什么。不幸的是,the docs中的唯一解释是:

  

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

这是什么意思?好吧,您可以尝试从第一原则开始,将其与未绑定方法的意义相提并论(当然,除非现有Python中的未绑定方法不是这样),或者您可以阅读{ {3}}或原始the C source

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

您无法以未绑定的方式明确使用未绑定的super (例如,str.split('123', '2')'123'.split('2')相同,但super(cls).foo(cls)super(cls, cls).foo()的工作方式不同。但你可以隐式使用它们 ,就像你对未绑定方法一样,而不用正常的思考它。

如果您不了解introduction to 2.2's class-type unification (including a pure-Python super clone),那么tl&#39;博士是:当您评估myobj.mymeth时,Python查找mymeth,找不到它在myobj本身,但确实在类型上找到它,因此会检查它是否为how methods work,如果是,则调用其__get__方法将其绑定到{{ 1}}。

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

未绑定的__get__是非数据描述符,其super方法返回绑定__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() ,将其粘贴在bs类型上,然后B是一个普通绑定超级,因此b.bsb.bs.f ,就像A.f方法中的super().f一样。

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

<子> 1。我在这里作弊。首先,未绑定的方法在Python 3中不再是一件事 - 但是函数的工作方式相同,因此Python使用它们来使用未绑定的方法。其次,作为C内置的super,即使在2.x中也不是一种不受限制的方法,但无论如何它至少就这一点而言,至少就我们所关注的而言。

<子> 2。实际上是普通的功能。