标题:Python里让人精神分裂的super class 出处:Felix021 时间:Thu, 22 Aug 2013 01:08:43 +0000 作者:felix021 地址:https://www.felix021.com/blog/read.php?2126 内容: 本篇来自这个问题:python中创建父类对象的问题 也许可以认为这是Python设计的缺陷导致的,因为Python 3.0只需要用 `super().some_attr` 就行了。至于为什么需要两个参数,可以大概分析一下,如果有错,欢迎指正。 先介绍下背景知识:super是从Python 2.2开始随着 `new-style class` 一起引入的,也只能应用于所谓的 `new-style class` (即直接或间接继承于 `object` 的class),可以在一定程度上解决所谓`钻石继承`的问题。2.2起所有python内置class都是new-style class。 那么super是什么呢?实际上它是一个builtin的type,你调用 `super(Base, self)` 会返回一个 `__class__ = super` 的对象,这个对象可以作为一个代理,通过BFS的顺序(实际上列表已经保存在`Base.__mro__`里了)去访问第一个**直接定义**了目标属性的基类里的属性。`super(type, obj_or_subtype).attr` 基本上可以认为是 `find_in_mro_provide_by(obj_or_subtype, "attr")` ,有点晦涩。 **注意**,super()返回的不是“父类对象”,而是一个super类型的对象;实例化的时候,第一个参数是一个类型(type),第二个参数可以是type的instance,也可以是type的subclass。 介绍完基础知识,可以先说说为什么要有第二个参数。理由很简单 —— 我们知道 self 代表当前对象,每个对象的方法在定义的时候都需要显示地把self作为第一个参数,你本来应该写成这样 some_class.some_method(obj, *args, **kwargs) 但是因为Python语法允许 `obj.some_metod(*args, **kwargs)` (本质上是个语法糖) ,所以你可以写得简单点(不必显式地给出方法的第一个参数)。而super对象则不同,它没有语法上的直接支持,所以在内部invoke some_method的时候必须指定某个对象,而这个对象的得你自己塞给它,也就是说把这个super对象绑定(bound)到第二个参数上。所以实际上并不是非要用self作为super的第二个参数,甚至super并不是必须在class内部才能调用: class A(object): def foo(self): print self.name class B(A): def __init__(self, name): self.name = name b1 = B('b1') super(B, b1).foo() #produces 'b1' 至于为什么要有第一个参数,如果你看了 `help(super)` 就会发现它居然提供了个单参数的版本: super(type) -> unbound super object 这个理解起来比较困难点。先说这个`unbound`,没绑定,就是说这个super对象没有实际绑定到某个对象上,它并不是可以直接用的。要怎么用呢?那就得注意到`help(super)`里面有一个 `__get__` 方法,也就是说,super类还是一个Descriptor类!哎,这个Descriptor类展开又是好大一段,简单地说就是它可以把自己和某个object的属性绑定,使得访问这个属性的时候,实际上是在调用它的`__get__/__set__`方法: class Descr(object): def __get__(self, obj, tp): #最后一句传进来的 obj=x, tp=X return 'get!' class X(object): t = Descr() x = X() print x.t #this produces 'get!' 回到unbound super, 为了使用它,就要通过 __get__ 方法把它再绑定到一个对象上。由于Descriptor的属性,特别适合这么用: class A(object): def foo(self): print 'foo' class B(A): def foo(self): self._super.foo() B._super = super(B) #这还不能直接写在B的定义里,多蛋疼啊。 b = B() b.foo() #produces 'foo' 这是它的一个可能用法。我不确定是否还有其他更合适的用途,但是在这里实际上也并不是很好,尤其是遇到staticmethod的时候还会出错,再加上绕了这么大一个弯,实在不是很推荐使用。 据说unbound super的使用非常少,不知道现实意义有多少,也不知道当初为什么设计成这个样子,总之由于Python3.0已经改了(虽然仍然保留了unbound super),所以基本上还是可以认为这是设计的历史遗留问题。 参考文献(unbound super主要参考了这个系列):Things to Know About Python Super [1] http://www.artima.com/weblogs/viewpost.jsp?thread=236275 [2] http://www.artima.com/weblogs/viewpost.jsp?thread=236278 [3] http://www.artima.com/weblogs/viewpost.jsp?thread=237121 Generated by Bo-blog 2.1.0