天天看點

Python-Beyond the Basics--Inheritance & Subtype PolymorphismMethod Resolution Order5.應用Built-In Super Function

最近在看pluralsight的 python- beyond the basics 教程,學到好多東西。在這裡記一下。

本節講的是繼承相關的知識。

Method Resolution Order

1. 定義

MRO 指明了在繼承中,當類本身和基類中同時有多個同名函數定義的時候,應該如何查找最終智行的函數。本身是ordering of the inheritance graph.

2. 檢視 YOUR_CLASS.__mro__ 或者是 YOUR_CLASS.mro() 差別在于__mro__産生的是tuple,mro()産生的是數組 任意class的mro 最後一個元素都是object class 例如:

<span style="font-family:Microsoft YaHei;">class A(object):
    def func(self):
        return 'A.func'

class B(A):
    def func(self):
        return 'B.func'

class C(A):
    def func(self):
        return 'C.func'

class D(C, B):
    pass
    
print D.mro()</span>
           

産生結果是:

<span style="font-family:Microsoft YaHei;">[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>]</span>
           

注:在python2.7.8下,發現當class A 定義時沒有繼承object,則檢視class的mro會報錯。

3. 使用 對obj.method() 調用來說, python會在class的MRO清單中的class裡依次查找class是否有比對的函數, 一旦找到,就調用對應class的函數來執行。 是以對于上面的class D 如果執行:

<span style="font-family:Microsoft YaHei;"><span style="font-family:Microsoft YaHei;font-size:14px;">d = D()
d.func()</span></span>
           

則得到:

<span style="font-family:Microsoft YaHei;">'C.func'</span>
           

當對D的繼承順序修改,交換B,C的位置,則執行結果就變成了 "B.func".

4.計算

C3--python中計算MRO的算法,參考網址:The Python 2.3 Method Resolution Order  and  C3 C3 makes sure:     1) subclass在 base class之前     2) base class from class definition is preserved     3) First two qualities are preserved no matter where you stat in the inheritance graph.

好像也不是很明白。根據第一個連結中,給出的計算方法:

對多繼承結構中的類C來說,C的基類有B1, B2, ....Bn. 要計算類C的線性值(Linearization) L[C], 規則如下: L[C] 是C加上 父類線性值和父類list的merge結果,公式如下: L[C (B1, B2, ...Bn)] = C +merge (L[B1], L[B2],.....  L[Bn],  B1,  B2, ....., Bn)

特别地, L[ object ] =  object.  

merge的核心描述是: take the head of the first list, i.e L[B1][0]; if this head is not in the tail of any of the other lists, then add it to the linearization of C and remove it from the lists in the merge, otherwise look at the head of the next list and take it, if it is a good head. Then repeat the operation until all the class are removed or it is impossible to find good heads. In this case, it is impossible to construct the merge, Python 2.3 will refuse to create the class C and will raise an exception. 對于隻有一個基類B的類C來說, L[C(B)] = C + merge(L[B],B) = C + L[B]。 文章: Python MRO C3 給出了詳細的解析。 具體的實作代碼在github 中:functools.py 函數:_c3_merge 。

<span style="font-family:Microsoft YaHei;">def _c3_merge(sequences):
    """Merges MROs in *sequences* to a single MRO using the C3 algorithm.
    Adapted from http://www.python.org/download/releases/2.3/mro/.
    """
    result = []
    while True:
        sequences = [s for s in sequences if s]   # purge empty sequences
        if not sequences:
            return result
        for s1 in sequences:   # find merge candidates among seq heads
            candidate = s1[0]
            for s2 in sequences:
                if candidate in s2[1:]:
                    candidate = None
                    break      # reject the current head, it appears later
            else:
                break
        if candidate is None:
            raise RuntimeError("Inconsistent hierarchy")
        result.append(candidate)
        # remove the chosen candidate
        for seq in sequences:
            if seq[0] == candidate:
                del seq[0]
</span>
           

文章: Python MRO C3 總結說“從動作上看就是捏住兩端,拉直。(每個節點間繩子具有最大彈性,保證拉直,最小為兩個節點多路徑下的最大步長)”還是挺形象的。

5.應用

"The 

__mro__

 attribute of the type lists the method resolution search order used by both 

getattr()

 and 

super()

."

即 getattr 和 super都利用了MRO來查找method

Built-In Super Function

1. 定義

在了解了MRO的基礎上,再看super就很容易了。 “Given a method resolution order and a class C,super() gives you a object which resolves methods using only the part of the MRO which comes after C”。 super() 其實是傳回了一個proxy對象,該對象是用來路由method調用的。 def super(type, object_or_type):      pass

2. 參數說明:

如果第二個參數被忽略,那麼super的調用就傳回的是unbound 對象,這種情況用的極少,pluralsight裡面直接跳過了,但是看到有人分析了這種用法,也許可以參考一下:Python裡讓人精神分裂的super class。 當第二個參數被指定的時候,稱之為bound proxy,這個是常見的。

對于bound proxy來說,有兩種類型:

1) instance-bound:

 super(class, instance-of-class), 要求instance必須是class的執行個體(isinstance(instance, class)=true)。 調用super時,流程如下:

  • 拿到第二個參數的類型對應的MRO
  • 找到第一個參數class在MRO中的位置
  • 然後使用該class在MRO之後的所有class來用于解析method

2) class-bound: 

super(base-class, derived-class), 要求derived-class 必須是base-class的子類(更準确的是issubclass(derived-class, base-class)=true),否則抛異常。 當調用super class bound proxy時,過程如下:

  • python 拿到 derived class的MRO
  • 然後在MRO裡面找到base class
  • 然後從base class後面的所有class中找到最先比對函數的那個類。

注意到查找比對類時,是從base class後面開始的,不包括base class本身。

3. 沒有參數的super() 

在python3.x中添加了無參數的super()函數,具體含義是跟調用上下文有關。 1) 在instance method中,等同于 super(class-of-method, self)  2 )  在class method中, 等同于 super( class-of-method, class)