1. 程式人生 > 程式設計 >python super的使用方法及例項詳解

python super的使用方法及例項詳解

功能

super功能:super函式是子類用於呼叫父類(超類)的一個方法。

用法

1.在子類 __init__() 方法中正確的初始化父類,保證相同的基類只初始化一次。

2.覆蓋特殊方法。

3.解決多重繼承中,子類重複呼叫父類方法的問題。

注意

super()繼承只能用於新式類,用於經典類時就會報錯。

新式類:必須有繼承的類,如果無繼承的,則繼承object

經典類:沒有父類,如果此時呼叫super就會出現錯誤:『super() argument 1 must be type,not classobj)

在子類__init__()方法中正確初始化父類,保證相同的基類只初始化一次

假如說在父類中實現了一個方法,你想在子類中使用父類的這個方法並且做一定擴充套件但是又不想完全重寫,並且這個場景中的繼承屬於多繼承,那麼super()就出場了,可以實現方法的增量修改。

A(父類)有x屬性,B(子類)想新增y屬性:

class A(object):
  def __init__(self,x):
    self.x = x
class B(A):
  def __init__(self,x,y):
    super(B,self,).__init__(x)
    self.y = y
a = A(2)
b = B(2,4)
print(a.x)
print(b.x,b.y)

覆蓋Python特殊方法

class Proxy:
  def __init__(self,obj):
    self._obj = obj

  # Delegate attribute lookup to internal obj
  def __getattr__(self,name):
    return getattr(self._obj,name)

  # Delegate attribute assignment
  def __setattr__(self,name,value):
    if name.startswith('_'):
      super().__setattr__(name,value) # Call original __setattr__
    else:
      setattr(self._obj,value)

在上面程式碼中,__setattr__() 的實現包含一個名字檢查。 如果某個屬性名以下劃線(_)開頭,就通過 super() 呼叫原始的 __setattr__() , 否則的話就委派給內部的代理物件 self._obj 去處理。 這看上去有點意思,因為就算沒有顯式的指明某個類的父類, super() 仍然可以有效的工作。

解決多重繼承中,子類重複呼叫父類方法的問題

class Base:
  def __init__(self):
    print('Base.__init__')
class A(Base):
  def __init__(self):
    Base.__init__(self)
    print('A.__init__')

儘管對於大部分程式碼而言這麼做沒什麼問題,但是在更復雜的涉及到多繼承的程式碼中就有可能導致很奇怪的問題發生。 比如,考慮如下的情況:

class Base:
  def __init__(self):
    print('Base.__init__')

class A(Base):
  def __init__(self):
    Base.__init__(self)
    print('A.__init__')

class B(Base):
  def __init__(self):
    Base.__init__(self)
    print('B.__init__')

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

如果你執行這段程式碼就會發現 Base.__init__() 被呼叫兩次,如下所示:

>>> c = C()
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
>>>

可能兩次呼叫 Base.__init__() 沒什麼壞處,但有時候卻不是。 另一方面,假設你在程式碼中換成使用 super() ,結果就很完美了:

class Base:
  def __init__(self):
    print('Base.__init__')
class A(Base):
  def __init__(self):
    super().__init__()
    print('A.__init__')

class B(Base):
  def __init__(self):
    super().__init__()
    print('B.__init__')

class C(A,B):
  def __init__(self):
    super().__init__() # Only one call to super() here
    print('C.__init__')

執行這個新版本後,你會發現每個 __init__() 方法只會被呼叫一次了:

>>> c = C()
Base.__init__
B.__init__
A.__init__
C.__init__
>>>

為了弄清它的原理,我們需要花點時間解釋下Python是如何實現繼承的。 對於你定義的每一個類,Python會計算出一個所謂的方法解析順序(MRO)列表。 這個MRO列表就是一個簡單的所有基類的線性順序表。例如:

>>> C.__mro__
(<class '__main__.C'>,<class '__main__.A'>,<class '__main__.B'>,<class '__main__.Base'>,<class 'object'>)
>>>

為了實現繼承,Python會在MRO列表上從左到右開始查詢基類,直到找到第一個匹配這個屬性的類為止。

而這個MRO列表的構造是通過一個C3線性化演算法來實現的。 我們不去深究這個演算法的數學原理,它實際上就是合併所有父類的MRO列表並遵循如下三條準則:

  • 子類會先於父類被檢查
  • 多個父類會根據它們在列表中的順序被檢查
  • 如果對下一個類存在兩個合法的選擇,選擇第一個父類

老實說,你所要知道的就是MRO列表中的類順序會讓你定義的任意類層級關係變得有意義。

當你使用 super() 函式時,Python會在MRO列表上繼續搜尋下一個類。 只要每個重定義的方法統一使用 super() 並只調用它一次, 那麼控制流最終會遍歷完整個MRO列表,每個方法也只會被呼叫一次。 這也是為什麼在第二個例子中你不會呼叫兩次 Base.__init__() 的原因。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。