天天看點

13 Python superPython 深入了解super的用法

Python 深入了解super的用法

  • Python 深入了解super的用法
    • 帶着問題去學super
    • super
    • 兩種調用父類方法的差別
      • 通過父類名直接調用父類方法
      • super
      • 代碼說明
    • 總結
    • 疑問
    • 參考網址

轉載請标明出處(http://blog.csdn.net/lis_12/article/details/52870728).

帶着問題去學super

Python中既然可以直接通過父類名調用父類的方法為什麼還會存在super函數?

比如

class Child(Parent):

def __init__(self):

​ Parent.__init__(self)

這種方式與super(Child, self).__init__()有差別呢?

準備知識:

MRO 全稱 Method Resolution Order(方法解析順序),它代表了類繼承的順序.

注:如果不懂MRO,參考這篇文章(http://blog.csdn.net/lis_12/article/details/52859376)

super()

謹記:super 指的是 MRO 中的下一個類! 不代表父類!!!!!!!

例如:MRO = [A,B,C,D]

super(A,self).__init__()代表A的下一個類B的__init__().

super()的代碼實作:

def super(cls, inst):
    mro = inst.__class__.mro()         #擷取inst的MRO
    return mro[mro.index(cls) + ]     #擷取cls在MRO中的索引,然後加1
           

參數說明:

  1. inst 負責生成 MRO 的 list;
  2. 通過 cls 定位目前 MRO 中的 index, 并傳回 MRO[index + 1];

這兩件事才是 super 的實質,一定要記住!

兩種調用父類方法的差別

通過父類名直接調用父類方法

class D(object):
    def __init__(self):
        print 'enter D'
        object.__init__(self)
        print 'leave D'

class C(D):
    def __init__(self):
        print 'enter C'
        D.__init__(self)
        print 'leave C'

class B(D):
    def __init__(self):
        print 'enter B'
        D.__init__(self)
        print 'leave B'

class A(B,C):
    def __init__(self):
        print 'enter A'
        B.__init__(self)
        C.__init__(self)
        print 'leave A'

if __name__ == '__main__':#主程式
    a = A()
    print A.__MRO__
'''
result:
enter A
enter B
enter D
leave D
leave B
enter C
enter D    初始化了兩次--!
leave D
leave C
leave A
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
           

super()

#!/usr/bin/python
# -*- coding: utf-8 -*-

class D(object):
    def __init__(self):
        print 'enter D'
        super(D,self).__init__()
        self.x = 'D'
        print 'leave D'

    def fun(self):
        print 'D fun()',self.x

class C(D):
    def __init__(self):
        print 'enter C'
        super(C,self).__init__()
        self.x = 'C'
        print 'leave C'

    def fun(self):
        print 'C fun()',self.x

class B(D):
    def __init__(self):
        print 'enter B'
        super(B,self).__init__()
        print 'leave B'

class A(B,C):
    def __init__(self):
        print 'enter A'
        super(A,self).__init__()
        self.x = 'A'
        print 'leave A'

if __name__ == '__main__':#主程式
    a = A()
    print A.__MRO__
    a.fun()  #按照MRO查找fun()方法,找到後停止查找,self.x = 'A'最後執行的是以self.x = A,如果把self.x = 'A'放到super(A,self).__init__(),self.x = 'C'就是最後執行的喽. 
'''
result:
enter A
enter B
enter C
enter D
leave D             D隻初始化一次
leave C
leave B
leave A
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
C fun() A
'''
           

代碼說明

通過父類名調用父類中的方法沒啥可說的,順序執行就好,然後就重複調用了父類D的__init__()方法- -!…重點說super.

使用super(A,self).__init__()執行流程:

  1. 首先進入類A的__init__()函數,列印’enter A’,當執行到super處,super(A,self)擷取MRO中A的下一個類B,并傳回,調用B.__init__();
  2. 進入B.__init__(),列印 ‘enter B’,當執行到super處,super(B,self)擷取MRO中B的下一個類C,并傳回,調用C.__init__();
  3. 進入C.__init__(),列印 ‘enter C’,當執行到super處,super(C,self)擷取MRO中C的下一個類D,并傳回,調用D.__init__();
  4. 進入D.__init__(),列印 ‘enter D’,當執行到super處,super(D,self)擷取MRO中D的下一個類object,并傳回,調用object.__init__();
  5. object.__init__()執行完成後,開始執行D.__init__()->C.__init__()->B.__init__()->A.__init__()中未執行的代碼,依次列印leave D C B A;

總結

使用super()優點:

  1. 保證不會重複調用父類的__init__()函數;
  2. 代碼更好維護,如果把父類的名字改了以後,使用類名調用方法還需要改類名,代碼可維護性太差,使用super則能很好的解決這些問題;

使用super()注意事項:

super要麼全用,要麼全不用,哪怕父類是object也要用super,千萬不要混用.

混用帶來的坑:

class D(object):
    def __init__(self):
        print 'enter D'
        super(D,self).__init__()

class C(D):
    def __init__(self):
        print 'enter C'
        super(C,self).__init__()

class B(D):
    def __init__(self):
        print 'enter B'
        D.__init__(self)

class A(B,C):
    def __init__(self):
        print 'enter A'
        super(A,self).__init__()


if __name__ == '__main__':#主程式
    a = A()
    print A.__MRO__
'''
result:
enter A
enter B
enter D
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
           

從以上代碼可知,沒有調用C的__init__()方法.

__init__()調用順序:A->B->D->object,繞過了C.

疑問

#!/usr/bin/python
# -*- coding: utf-8 -*-
class Root(object):
    def __init__(self):
        print("this is Root")

class B(Root):
    def __init__(self):
        print("enter B")
        # print(self)  # this will print <__main__.D object at 0x...>
        super(B, self).__init__()
        print("leave B")

class C(Root):
    def __init__(self):
        print("enter C")
        super(C, self).__init__()
        print("leave C")

class D(B, C):
    pass

d = D()
print(d.__class__.__MRO__)
'''
enter A    D中沒有重定義__init__()函數是怎麼調用的呢?
enter B
enter D
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
           

個人了解:D中預設的__init__()使用了super()……如果有大神知道原因的話求指導,謝謝…

參考網址

  1. https://www.laike9m.com/blog/li-jie-python-super,70/
  2. https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
  3. http://blog.csdn.net/lis_12/article/details/52859376