我试图从__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(cls)改为super(cls, cls)。使用单参数形式,您最终会在foo的父类(即object中)中查找foo方法。
- 你为什么在这里使用super(cls)?未绑定的supers很少有用,它们不是您在这里要找的。如果你只是想调用cls的foo类方法的父类,那就是super(cls, cls).foo(),就像调用一个方法实例一样。如果你想做些不同的事…那又怎样?
- 如果你真的想要一个解释为什么这不做你想要的,在解释之前你需要一些背景。你知道什么是描述符,方法一般如何工作,超级如何工作,只需要知道超级如何工作?或者你需要解释其中一个更基本的部分吗?
- @abarnet不可否认,我以前从未使用过unbound super,可能会被docs弄糊涂:super([type[,object or type]])返回一个代理对象,它将方法调用委托给类型的父类或同级类。[…]。所以我可能需要一个关于这方面的更新;其他我熟悉的。但最终你所说的是有意义的。
一个普通的super对象(您通常从称为super(MyType, self)或super()或super(MyType, myobj)中得到的)跟踪它创建的类型和对象。每当您查找super上的属性时,它会按方法解析顺序跳过MyType,但如果找到方法,它会将其绑定到该self对象。
未绑定的super没有self对象。因此,super(cls)跳过mro中的cls找到方法foo,然后将其绑定到……oops,它没有什么可调用的。
那么,你能称之为classmethodon的是什么?类本身或其子类,或该类或子类的实例。因此,其中任何一个都将成为super的第二个论点,最明显的一个论点是:
这与静态方法(绑定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.f是A.f,就像super().f是在B方法中一样。
你为什么要这么做?我不确定。我已经用python编写了各种荒谬的动态和反射代码(例如,对于其他解释器的透明代理),我记不起需要一个未绑定的super。但如果你需要它,它就在那里。
>1。我在这里有点作弊。首先,在Python3中,未绑定方法不再是一回事,但是函数的工作方式相同,所以Python在使用未绑定方法的地方使用它们。其次,str.split是C内置的,即使在2.x中,它也不是一种适当的未绑定方法,但它的行为无论如何都像一种方法,至少就我们这里所关注的那样。
>2。实际上是简单的旧函数。
- 谢谢你的详细解释。尤其是最后一条注释"和未绑定的super是非数据描述符,它们的__get__方法返回绑定的super。"点击。
- @客人是的,这才是关键——"未绑定的X"表示"X.__get__"返回绑定的x。除了一般规则没有在任何地方声明,而且在任何地方记录的唯一特殊情况在Python3中都不再存在,所以…不是最容易发现的规则。顺便说一下,我认为应该有更好的方法来组织这个答案,这样会帮助更多的人,但我想不出它是什么;如果你有什么想法,那就太好了。
- 也许这是一个更普遍的问题,关于绑定和未绑定超级之间的相互作用?有一个关于unbound super的问题,但是答案是完全否定的;没有其他的,所以资源真的在处理这个主题。但总的来说,我认为我应该从解释MRO和激励(绑定的)super的使用开始,然后切换到unbound super,解释它是如何创建的,它是什么(连同描述符),转换为bound super,它的目的是什么,以及一些用例(尽管这些看起来很难找到)。
- @一个客人,肯定会更清楚,但似乎这可能是太长的一个这样的答案(特别是与我的冗长)。它可能会成为一篇有趣的博客文章,但是如果没有一些未绑定超级的用例,它仍然很难被激发。至于它的用途,我怀疑它可能就在那里,因为一旦构建了描述符和Superforpython2.2,实现起来就很简单,所以guido只是实现了它,而不必担心是否有用例。这在现代的Python中可能不会发生,但当时情况不同。