随便评论一下 發表於 2019-5-25 17:55:00

python中的super()用法以及多继承协同任务

<p>理解了python的MRO之后,我们就可以更加准确地使用super()函数,以及使用super()完成<strong>多继承协同任务</strong></p>
<p>super().method()是调用父类中的方法,这个搜索顺序当然是按照MRO从前向后开始进行的</p>
<p><code>super([, object-or-type])</code><br>
根据官方文档,<code>super</code>函数返回一个委托类<code>type</code>的父类或者兄弟类方法调用的代理对象。<code>super</code>函数用来调用已经再子类中重写过的父类方法。</p>
<p>这句话其实很难看明白,为什么除了父类还可能是兄弟类?</p>
<p>要理解这句话,先谈谈<code>super</code>的参数的传入方式不同带来的不同之处</p>
<p>常见的是直接调用<code>super()</code>,这其实是<code>super(type, obj)</code>的简写方式,将当前的类传入<code>type</code>参数,同时将实例对象传入<code>type-or-object</code>参数,这两个实参必须确保<code>isinstance(obj, type)</code>为<code>True</code>。</p>
<p>使用该方法调用的<code>super</code>函数返回的代理类是<code>obj</code>所属类的MRO中,排在type之后的下一个父类。</p>
<p>示例:<br>
类的继承结构如下<br>
<img src="https://img2018.cnblogs.com/blog/1249301/201905/1249301-20190525170912709-1726985906.png"></p>
<pre><code class="language-python">class A: pass
class B: pass
class C(A,B): pass
</code></pre>
<p>类C的MRO为</p>
<p>现在我们为其添加一个方法<code>x()</code></p>
<pre><code class="language-python">class A:
        def x(self):
                print('run A.x')
                super().x()
                print(self)

class B:
        def x(self):
                print('run B.x')
                print(self)
               
class C(A,B):
        def x(self):
                print('run C.x')
                super().x()
                print(self)

C().x()
</code></pre>
<p>该方法最先出现是作为C的实例方法,根据MRO,我们很清楚,下一步它会调用其MRO父类中的同名方法,即A中的x()方法,但是,我们在A的x()方法中再次使用了super(),这时候会怎么样呢?</p>
<p>查看结果输出</p>
<pre><code class="language-python">run C.x
run A.x
run B.x
&lt;__main__.C object at 0x000002B5041BB710&gt;
&lt;__main__.C object at 0x000002B5041BB710&gt;
&lt;__main__.C object at 0x000002B5041BB710&gt;
</code></pre>
<p>在调用了A中的<code>x()</code>方法之后,下一个调用的是B中的<code>x()</code>方法,在继承结构中,类A和类B互为兄弟关系,<code>super()</code>在A中调用的时候,最终却调用其兄弟的同名方法,这就是之前说的,<em><code>super</code>函数返回一个委托类<code>type</code>的父类或者兄弟类方法调用的代理对象</em>。</p>
<p>那么,为什么?<br>
根据print(self)的输出,所有在这些super()的调用过程中,self参数传入的是同一个obj,就是我们初始化的C(), 在内存中位置为0x000002B5041BB710的实例对象。</p>
<p>之前已经说过,<code>super()</code>是<code>super(type, obj)</code>的简写,在调用<code>super()</code>时,<code>type</code>参数传入的是当前的类,而<code>obj</code>参数则是默认传入当前的实例对象,在<code>super()</code>的后续调用中,<code>obj</code>一直未变,而实际传入的<code>class</code>是动态变化,不过,在首次调用时,MRO就已经被确定,是<code>obj</code>所属类(即C)的MRO,因此<code>class</code>参数的作用就是从已确定的MRO中找到位于其后紧邻的类,作为再次调用<code>super()</code>时查找该方法的下一个类。<br>
即,<code>super</code>函数这一部分的核心逻辑应该为</p>
<pre><code class="language-python">def super(class, obj):
    mro_list = obj.__class__.mro()
    next_parent_class = mro_list
    return next_parent_class
</code></pre>
<p>这就是为什么必须保证<code>isinstance(obj, type)</code>为<code>True</code>的原因,如果不是,那么可能<code>type</code>就不存在于<code>obj.__class__</code>的MRO列表中,该算法就无法正确找到下一个应当被查找的类。</p>
<p>因此,如果我们在某个类的父类中按照其MRO顺序,每个父类都写一个同名方法,同时每个该方法中都继续调用<code>super()</code>,直到在MRO列表<code>object</code>之前的最后一个类的同名方法中不再调用<code>super()</code>,那么在调用该方法时,会在各个父类中按照MRO列表的顺序依次被调用,这个过程中存在数据的传递,代表它们之间可以<strong>共享</strong>某些数据,这就实现了多继承协同工作。</p>
<p>而这种工作方式,通过重写方法是根本无法实现的。</p>
<p>使用实例:</p>
<p>继承结构如下图<br>
<img src="https://img2018.cnblogs.com/blog/1249301/201905/1249301-20190525173624849-2039140699.png"></p>
<p>我们试图达到的目的如下:<br>
一个类<code>Final</code>继承<code>Header</code>以获得属性<code>header</code><br>
同时我们通过混合其他类来快捷地修饰<code>header</code>属性,例如继承类<code>Mixin1</code>会为<code>header</code>属性(其数据类型为列表)追加数据<code>data1</code>,而继承类<code>Minix2</code>则会为<code>header</code>属性的头部添加元素<code>data2</code>,注意,因为这些操作并不冲突,这些行为都不该相互覆盖。</p>
<pre><code class="language-python">class Minix1:
        """该混合类为header列表末尾添加data1"""
        def get_header(self):
                print('run Minix1.get_header')
                ctx = super().get_header()
                ctx.append('data1')
                return ctx

class Minix2:
        """该混合类为header列表头部添加data2"""
        def get_header(self):
                print('run Minix2.get_header')
                ctx = super().get_header()
                ctx.insert(0, 'data2')
                return ctx

class Header:
        header = []
        def get_header(self):
                print('run Headers.get_header')
                return self.header if self.header else []


class Final(Minix1, Minix2, Header):

        def get_header(self):
                return super().get_header()
</code></pre>
<p>当然,我们可以定义更多的混合类,并从中选取所需的类来快速得到想要的<code>header</code>属性, 在这个例子中,这两个混合类已经足够说明问题。</p>
<p>我们现在使用类C的<code>get_header()</code>方法来得到其<code>header</code>属性</p>
<pre><code class="language-python">print(Final.mro())
#
header = Final().get_header()
#run Minix1.get_header
#run Minix2.get_header
#run Headers.get_header
print(header)
#['data2', 'data1']
</code></pre>
<p>看来,运行得很成功,我们实现了多继承协同工作的目标,通过混合不同个类,来模块化地快速得到想要的<code>header</code>属性。<br>
而这种工作方法,通过单纯的重写某个方法根本无法实现的,因为重写任何方法,它会在MRO列表中找到最优先(也就是最靠前)的拥有同名方法的类,然后调用该方法,并且终止检索,某项属性仅仅会被<strong>一个</strong>方法所影响。</p>
<p>相关文章或参考:<br>
Python进阶-继承中的MRO与super<br>
python 继承与多重继承</p><br><br>
来源:https://www.cnblogs.com/miyauchi-renge/p/10923127.html
頁: [1]
查看完整版本: python中的super()用法以及多继承协同任务