美文网首页
在 python 中使用 super 继承

在 python 中使用 super 继承

作者: eeert2 | 来源:发表于2020-04-22 09:56 被阅读0次

一、引言

python语言的三大特性:封装继承多态

使用继承可以提升代码的重用率。

举个栗子:
使用django的过程中,重写模型的.save().delete()方法是很常见的。

from django.db import models


class Image(models.Model):
    name = models.CharField('图片名称', max_length=32)
    path = models.CharField('图片的文件存储地址', max_length=64)
    size = models.IntegerField('图片的大小')

    def delete(self, using=None, keep_parents=False):
        # 从文件存储中删除图片
        ...
        
        # 调用父类的删除方法
        return super().delete(using=None, keep_parents=False)

二、为什么在python中使用super

在继承中,调用父类的方法有两种:直接通过父类名称调用通过super()调用,由于python的多继承设计,使用第一种方式在特殊的情况下可能会引起意想不到的问题。

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__')

Base.__init__只执行一次。

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

三、谈谈python的多继承与MRO

使用super()关键字为什么可以避免以上问题。因为使用super()后,python会按照MRO去调用父类。

如果你接触过java语言,那对单继承应该很熟悉,java中有super关键字,而python中使用super也能实现同样的效果。这是因为MRO将类的多继承关系碾平成一条固定的单继承链,

python设计为多继承,我们在初始化父类时就有一个先后顺序的问题。例如在上述的例子中:

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

# 我们也可以这样写:
class C(A,B):
    def __init__(self):
        B.__init__(self)
        A.__init__(self)

我们自己控制初始化就很容易出错,例如下面的多继承关系,我们如何控制去重[例如object就不应该被初始化多次],而且继承的先后顺序是固定统一的,让你的同事一眼就能知道。

截屏2020-04-2209.19.36.png

对于上述问题,python使用方法解析顺序(Method Resolution Order)也就是MROMRO将类的多继承碾平成一条固定的继承链,[顺序固定,去重]。

这样我们在使用时就可以看成一个单继承来使用。

例如上述的菱形继承,我们查看mro链式关系

if __name__ == '__main__':
    print(C.__mro__)
    # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)

打印结果为C -> A -> B -> Base -> object

我们应始终使用super().__init__()按照mro的先后关系去一一初始化,而不是自己创造一种初始化方式。

  • mro的一种运用方式。
class A:
    def spam(self):
        print('A.spam')
        super().spam()

直接使用会报错:

>>> a = A()
>>> a.spam()
A.spam
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 4, in spam
AttributeError: 'super' object has no attribute 'spam'
>>>

如果在多继承中就不会报错:

>>> class B:
...     def spam(self):
...         print('B.spam')
...
>>> class C(A,B):
...     pass
...
>>> c = C()
>>> c.spam()
A.spam
B.spam
>>>

你可以看到在类A中使用 super().spam() 实际上调用的是跟类A毫无关系的类B中的 spam() 方法。 这个用类CMRO列表就可以完全解释清楚了:

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

在类CMRO列表中,B是作为A的父类排列的,所以类A中使用 super().spam() 起了作用。

相关文章

  • python 神奇的super()

    我们都知道在python继承中,子类调用父类的方法是使用super()函数,在2.X里格式是super(mycla...

  • 怎么理解Python类中的super函数

    前言 在Python类的继承中,经常能看到super函数的存在,那super函数主要的作用,以及如何理解和使用好这...

  • Python进阶-继承中的MRO与super

    Python进阶-继承中的MRO与super @(Python)[python, python进阶] [TOC] ...

  • 在 python 中使用 super 继承

    一、引言 python语言的三大特性:封装,继承,多态 使用继承可以提升代码的重用率。 举个栗子:使用django...

  • ubuntu 下基于python深度学习

    一.python 的基本语法 <1>Python 类的定义、继承及使用对象 注意super()的使用,成员函...

  • python super方法,MRO详解

    python2和python3中super的使用: python2中的super只适用与新式类,在早期的pytho...

  • 1、继承中super和this关键字的使用

    继承中super和this关键字的使用 super关键字的使用 在子类的成员方法中,访问父类的成员变量。 在子类的...

  • 类访问权限

    java中仅有单继承,没有多继承。 java中使用super来调用父类中的指定操作: super可以用于访问父类中...

  • Python - super()

    参考:Python : super没那么简单 一.单继承 1.Python2 2.Python3 二.多继承

  • Python的继承

    #Python继承的特点 总是从某个类继承 不要忘记调用 super().init 一定要用 super(Teac...

网友评论

      本文标题:在 python 中使用 super 继承

      本文链接:https://www.haomeiwen.com/subject/nwauihtx.html