英文文檔: super([type[, object-or-type]]) Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for acces ...
英文文檔:
super
([type[, object-or-type]])
Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr()
except that the type itself is skipped.
The __mro__
attribute of the type lists the method resolution search order used by both getattr()
and super()
. The attribute is dynamic and can change whenever the inheritance hierarchy is updated.
If the second argument is omitted, the super object returned is unbound. If the second argument is an object, isinstance(obj, type)
must be true. If the second argument is a type, issubclass(type2, type)
must be true (this is useful for classmethods).
There are two typical use cases for super. In a class hierarchy with single inheritance, super can be used to refer to parent classes without naming them explicitly, thus making the code more maintainable. This use closely parallels the use of super in other programming languages.
The second use case is to support cooperative multiple inheritance in a dynamic execution environment. This use case is unique to Python and is not found in statically compiled languages or languages that only support single inheritance. This makes it possible to implement “diamond diagrams” where multiple base classes implement the same method. Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).
Note that super()
is implemented as part of the binding process for explicit dotted attribute lookups such as super().__getitem__(name)
. It does so by implementing its own __getattribute__()
method for searching classes in a predictable order that supports cooperative multiple inheritance. Accordingly, super()
is undefined for implicit lookups using statements or operators such as super()[name]
.
Also note that, aside from the zero argument form, super()
is not limited to use inside methods. The two argument form specifies the arguments exactly and makes the appropriate references. The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.
說明:
1. super函數返回的是一個代理對象,通過此對象可以調用所在類的父類或者兄弟類的方法,而不顯示的指定父類或者兄弟類的類名。
2. 為什麼要有super?
最早之前,在子類(B)中調用父類(A)的方法採用的方式如下:
#定義父類A >>> class A(object): def __init__(self): print('A.__init__') #實例化A >>> a = A() A.__init__ # 定義子類B,繼承A,在B的__init__ 方法中調用A的__init__方法 >>> class B(A): def __init__(self): print('B.__init__') A.__init__(self) #實例化B >>> b = B() B.__init__ A.__init__
假設現在要更改新定義一個類(A1),並更改繼承關係(B->A改成B->A1),則需要所有類中做如下修改:
#定義新的父類A1 >>> class A1(object): def __init__(self): print('A1.__init__') #更改繼承關係B->A改成B->A1 >>> class B(A1): def __init__(self): print('B.__init__') A1.__init__(self) #能正確調用新的父類A1的__init__方法 >>> b = B() B.__init__ A1.__init__ #假設忘了修改A.__init__(self) >>> class B(A1): def __init__(self): print('B.__init__') A.__init__(self) #則還是調用了A的__init__方法 >>> b = B() B.__init__ A.__init__
引入super之後,不需要顯示指定父類的類名,增強了程式的可維護性:
#B->A 改用super方式調用父類方法 >>> class B(A): def __init__(self): print('B.__init__') super().__init__() #能正確調用父類方法 >>> b = B() B.__init__ A.__init__ #更改繼承關係B->A改成B->A1,調用父類方法方式不用修改 >>> class B(A1): def __init__(self): print('B.__init__') super().__init__() #也能正確調用父類方法 >>> b = B() B.__init__ A1.__init__
3. 不帶任何參數的super等效於super(類名,self),此種情況多用於單繼承關係的子類中。
#super不帶參數 >>> class B(A1): def __init__(self): print('B.__init__') super().__init__() #能正確調用父類方法 >>> b = B() B.__init__ A1.__init__ #super帶兩個參數(類名,self) >>> class B(A1): def __init__(self): print('B.__init__') super(B,self).__init__() #也能正確調用父類方法 >>> b = B() B.__init__ A1.__init__
4. 如果第2個參數不傳入,則表示代理對象不綁定繼承關係。
#super第2個參數不傳入,生成代理對象不綁定繼承關係 >>> class B(A1): def __init__(self): print('B.__init__') super(B).__init__() #super(B).__init__()方法執行時不會調用父類方法 >>> b = B() B.__init__
5. 如果第2個參數是一個對象,則對象必須是第1個參數指定類型的實例,此種關係多用於多層繼承關係的子類中。
#定義父類A >>> class A(object): def __init__(self): print('A.__init__') #定義子類B,繼承A,__init__中調用父類的__init__方法 >>> class B(A): def __init__(self): print('B.__init__') super().__init__() #定義子類C,繼承B,__init__中調用父類的__init__方法 >>> class C(B): def __init__(self): print('C.__init__') super().__init__() #實例化C時,執行C的__init__方法,調用直接父類B的__init__方法,又進一步調用間接父類A的__init__方法 >>> c = C() C.__init__ B.__init__ A.__init__ #重新定義子類C,繼承關係不變,調用父類方法__init__時改用super(B,self) >>> class C(B): def __init__(self): print('C.__init__') super(B,self).__init__() #實例化C時,執行C的__init__方法,super(B,self)代理找到B的父類A,將self轉換成B的實例,直接調用了A的__init__方法,跳過了調用B的__init__方法 >>> c = C() C.__init__ A.__init__ #定義一個新類D >>> class D(object): def __init__(self): print('D.__init__') #重新定義C,繼承關係不變,調用父類方法__init__時改用super(D,self) >>> class C(B): def __init__(self): print('C.__init__') super(D,self).__init__() #實例化C時,執行C的__init__方法,super(D,self)代理找到D的父類object,將self轉換成D的實例,因為D和C無繼承關係,self 無法轉換成D的實例所以報錯 >>> c= C() C.__init__ Traceback (most recent call last): File "<pyshell#14>", line 1, in <module> c= C() File "<pyshell#13>", line 4, in __init__ super(D,self).__init__() TypeError: super(type, obj): obj must be an instance or subtype of type
6. 如果第2個參數時一個類型,則類型必須是第1個參數指定類型的子類,此種關係多用於多層繼承關係的子類中,適用於類方法。
#定義父類A,並定義有一個類方法sayHello >>> class A(object): @classmethod def sayHello(cls): print('A.sayHello') # 定義子類B,繼承A,重寫類方法sayHello,在其中調用父類的sayHello方法 >>> class B(A): @classmethod def sayHello(cls): print('B.sayHello') super().sayHello() # 定義子類C,繼承B,重寫類方法sayHello,在其中調用父類的sayHello方法 >>> class C(B): @classmethod def sayHello(cls): print('C.sayHello') super().sayHello() #調用C的類方法sayHello,其調用C的直接父類B的類方法sayHello,調用時B的sayHello方法又調用B的直接父類A的類方法sayHello >>> C.sayHello() C.sayHello B.sayHello A.sayHello #重新定義類C,繼承關係不變,使用super(C,C)的方式調用父類方法 >>> class C(B): @classmethod def sayHello(cls): print('C.sayHello') super(C,C).sayHello() #調用C的類方法sayHello,super(C,C)代理對象,找到C的直接父類B,然後調用C的直接父類B的類方法sayHello,調用時B的sayHello方法又調用B的直接父類A的類方法sayHello >>> C.sayHello() C.sayHello B.sayHello A.sayHello #重新定義類C,繼承關係不變,使用super(B,C)的方式調用父類方法 >>> class C(B): @classmethod def sayHello(cls): print('C.sayHello') super(B,C).sayHello() #調用C的類方法sayHello,super(B,C)代理對象,找到B的直接父類A,然後調用B的直接父類A的類方法sayHello,中間不會調用B的sayHello方法 >>> C.sayHello() C.sayHello A.sayHello #定義一個新類D,和A、B、C無繼承關係 >>> class D(object): @classmethod def sayHello(cls): print('D.sayHello') #重新定義類C,繼承關係不變,使用super(D,C)的方式調用父類方法 >>> class C(B): @classmethod def sayHello(cls): print('C.sayHello') super(D,C).sayHello() #調用C的類方法sayHello,super(D,C)代理對象,找到B的直接父類object,然後將C轉換成D類,轉換失敗調用出錯 >>> C.sayHello() C.sayHello Traceback (most recent call last): File "<pyshell#81>", line 1, in <module> C.sayHello() File "<pyshell#80>", line 5, in sayHello super(D,C).sayHello() TypeError: super(type, obj): obj must be an instance or subtype of type