Python-Beyond the Basics--Inheritance & Subtype Polymorphism

最近在看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
例如:
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()

产生结果是:
[, , , , ]

注:在python2.7.8下,发现当class A 定义时没有继承object,则查看class的mro会报错。

3. 使用
对obj.method() 调用来说, python会在class的MRO列表中的class里依次查找class是否有匹配的函数,
一旦找到,就调用对应class的函数来执行。
因此对于上面的class D 如果执行:
d = D()
d.func()
则得到:
'C.func'
当对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 。
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]
文章: 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)







你可能感兴趣的:(python)