0
class DahlBookManager(models.Manager):
    def get_query_set(self):
        return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')

这段自定义模型manager的代码里面 用super(DahlBookManager, self)这样的方式构造一个父类对象为什么要传一个DahlBookManager类名和self引用啊? DahlBookManager这个作为参数准确的说是一个DahlBookManager的对象吧? 也就是想问这个构造父类对象的super方法中为什么要传入这样两个参数?

ccc 422
2013-08-21 提问
1 个回答
2

也许可以认为这是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

撰写答案

推广链接