简介Python之super的用法及原理

来源:互联网 发布:90后创业开淘宝店 编辑:程序博客网 时间:2024/05/21 14:40

1. super()的功能

看下面代码:

class B1(object):    def __init__(self):        self.b1 = 10class B2(object):    def __init__(self):        self.b2 = 20class S1(B1, B2):    def __init__(self):        B1.__init__(self)        B2.__init__(self)        self.s1 = 30if __name__ == "__main__":    mys1 = S1()    print mys1.b1, mys1.b2, mys1.s1    # 10 20 30

以上是Python2.2之前的写法。
此时,如要将S1的父类B2改成B3,则__init__()中所有和B2相关的都要改成B3的;
并且,由以上也能看出,所有父类的__init__()都要显式地列在S1的__init__()中。

看Python2.2及其之后的写法(Python2.2引入了super())

class B1(object):    def __init__(self):        super(B1, self).__init__()        self.b1 = 10class B2(object):    def __init__(self):        super(B2, self).__init__()        self.b2 = 20class S2(B1, B2):    def __init__(self):        super(S2, self).__init__()        self.s2 = 30if __name__ == "__main__":    mys2 = S2()    print mys2.b1, mys2.b2, mys2.s2

由以上代码可以看出,如要将S1的父类由B2改成B3,则S2的代码几乎没有什么变动(只有第一行类的定义时,将B2改成B3即可)。
要注意的是,在B1和B2的__init__()函数中都用了super(),那么在S2的__init__()函数中也用super(),则只需写一行super()即可,而不用再显式调用所有父类的__init__()函数了。

2. 注意事项

  • 要么采用Python2.2之前的写法,全部显式调用父类的构造器;要么全部用super();不能混用。
  • super、MRO都是针对新式类的;如果是传统类,还是采用上面第一段代码中的写法吧。

3. super()的原理

super()其实做的就是这件事:

def super(cls, inst):    mro = inst.__class__.mro()    return mro[mro.index(cls) + 1]

由以上代码:
1. inst负责生成MRO的list;
2. 定位到当前cls在MRO中的位置,然后后移一位,返回此处的类;

MRO就是Method Resolution Order,详见这篇文章The Python 2.3 Method Resolution Order.

请看下面这个钻石继承的例子:

class A(object):    def __init__(self):        print "In A's __init__()"class B(A):    def __init__(self):        print "Enter B's __init__()"        super(B, self).__init__()        print "Leave B's __init__()"class C(A):    def __init__(self):        print "Enter C's __init__()"        super(C, self).__init__()        print "Leave C's __init__()"class D(B, C):    passd = D()print d.__class__.__mro__

程序的输出如下:

$ python test_super.pyEnter B's __init__()Enter C's __init__()In A's __init__()Leave C's __init__()Leave B's __init__()(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

从以上程序,可以清楚地看出,在上面这个钻石继承中,最后对于类D来说,其MRO就是 D B C A,故在执行了类B的构造器后,就去执行了类C的构造器,而不是类A的构造器。

关于MRO的一点小总结

  • 基类总是出现在派生类的后面;
  • 如果有多个基类,基类的顺序保持不变。

4. 参考文献

  • Python的内置函数super()与类方法(函数)继承
  • 有关super的问答
  • Things to know about Python super
  • Python’s super() considered super!
  • super on stackoverflow
  • The Python 2.3 Method Resolution Order
1 0