python进阶之Python的方法解析顺序

背景,这部分知识主要体现在继承的问题上,当我们使用继承时,这是我们需要考虑的问题,这个在使用super()上尤为重要.
值得注意的是,python3和python2在方法的解析顺序是不一样的,python2作为时代的弃儿,我这里就不解释了,这里着重解释python3的方法解析顺序.
知识点:Python的方法解析顺序是基于C3,这是为Dylan编程语言(http://opendylan.org)构建的MRO,而python对MRO的解释则出现在:自定义类这种类型一般通过类定义来创建 (参见 类定义 一节)。每个类都有通过一个字典对象实现的独立命名空间。类属性引用会被转化为在此字典中查找,例如 C.x 会被转化为 C.dict[“x”] (不过也存在一些钩子对象以允许其他定位属性的方式)。当未在其中发现某个属性名称时,会继续在基类中查找。这种基类查找使用 C3 方法解析顺序,即使存在 ‘钻石形’ 继承结构即有多条继承路径连到一个共同祖先也能保持正确的行为。有关 Python 使用的 C3 MRO 的详情可查看配合 2.3 版发布的文档 https://www.python.org/download/releases/2.3/mro/.
官方解释戳这里

python进阶之Python的方法解析顺序_第1张图片

关于上文中钻石形结构的示例图:

python进阶之Python的方法解析顺序_第2张图片

代码示例:
class CommonBase:
  def method(self):
    print('CommonBase')

class Base1(CommonBase):
  pass

class Base2(CommonBase):
  def method(self):
    print('Base2')

class MyClass(Base1,Base2):
  pass

MyClass().method()
=====> Base2
从结果来看,结果是从最短路径中得到的,若是在python2,会得到CommonBase,众所周知,super的超级继承,正是示例中的多重继承的方式,那么了解python3的方法解析顺序,对你使用super会有极大的帮助.
python3内置__mro__解释:

python进阶之Python的方法解析顺序_第3张图片

从上面来看,python3为我们展示的是符合现代编程语言的最短路径,那么它是如何实现的呢?
实际上python3 MRO是对基类递归调用:
L[MyClass(Base1,Base2)] = MyClass + merge(L[Base1],L[Base2],Base1,Base2)

这里L[MyClass]是MyClass类的线性化,而merge是合并多个线性化结果的具体算法

C的线性化是C加上父类的线性化和父类列表的合并的总和。

听起来有点拗口,其实是这样子发生的,首先取第一个列表的表头(head(表头)是列表的第一个元素,而tail(表尾)则包含其余元素。例如,在(Base1,Base2,……,BaseN)中,Base1是head,而(Base2,……,BaseN)则是tail),即L[Base1][0]。如果这个表头不在其他任何列表的表尾(tail),那么就将它添加到Myclass的线性化中,并从合并的列表(merge)里删除;否则的话,查看下一个列表的表头,如果是一个好的表头就将其取出。然后重复这一操作,直到所有的类都被删除或者找不到好的表头为止。在后一种情况下,无法构建合并,Python 3将拒绝创建MyClass类,并引发一个异常。

也就是说C3对每个父类进行递归深度查找以得到一个列表序列。然后,如果某个类包含在多个列表中,它会利用层次结构消歧计算出从左到右的规则,以此合并所有列表.

我们可以写个方法了展现这一结果:

def L(klass):  # klass 避免触碰class关键字
  return [k.__name__ for k in klass.__mro__]
L(MyClass)
====> ['MyClass','Base1','Base2','CommonBase','object']  
# object python中所有内置的祖先,所以会出现在最末尾

而类的__mro__则保存了这一结果,只有在这个时候,类才会完成加载,你可以调用 MyClass.mro()来得到计算结果.

所以python中的mro,其实指的是就是C3方法,理解C3了,你就能使用super()时,知道哪些会先执行,哪些会在后面~

python进阶之Python的方法解析顺序_第4张图片

你可能感兴趣的:(性能,编程思想,python,docker,容器,运维)