• python super()详解,一篇文章告诉你python的super是什么,如何使用


    如何做到子类中调用父类的同名方法

    假如你了解面对对象的继承会发现,当我们子类继承一个父类的时候,在子类中重写与父类同名的方法,那么python在这个子类调用这个同名方法时,会使用子类的方法。即子类的方法会把父类的方法覆盖掉(c++则需要定义虚函数virtual)。那有什么方法可以在子类中调用父类的同名方法呢?

    • 通过下面这个例子我们可以来看看c++是怎么做的
    class Base {
    public:
    	Base()
    	{
    		m_A = 100;
    	}
    
    	void func()
    	{
    		cout << "Base - func()调用" << endl;
    	}
    
    	void func(int a)
    	{
    		cout << "Base - func(int a)调用" << endl;
    	}
    
    public:
    	int m_A;
    };
    
    
    class Son : public Base {
    public:
    	Son()
    	{
    		m_A = 200;
    	}
    
    	//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
    	//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
    	void func()
    	{
    		cout << "Son - func()调用" << endl;
    	}
    public:
    	int m_A;
    };
    
    void test01()
    {
    	Son s;
    
    	cout << "Son下的m_A = " << s.m_A << endl;
    	cout << "Base下的m_A = " << s.Base::m_A << endl;
    
    	s.func();
    	s.Base::func();
    	s.Base::func(10);
    
    }
    int main() {
    
    	test01();
    
    	system("pause");
    	return EXIT_SUCCESS;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 我们会发现c++的做法是在调用同名成员时加一个Base::的作用域来实现在子类中调用和父类同名的成员

    那python是如何处理的呢?

    class Animal(object):
    
        def eat(self):
            print("动物吃东西")
    
    class Cat(Animal):
    
        def eat(self):
            print("猫吃东西")
    
        def eat1(self):
            Animal.eat(self)
    
    if __name__ == '__main__':
        c = Cat()
        c.eat()
        c.eat1()
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    猫吃东西
    动物吃东西
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 根据c++的思想,我们可以很容易的想到直接在子类使用父类名来调用这个方法,但是你在将来维护代码的时候会发现,当我们想改父类的名字的时候,我们会发现我们需要在子类也改父类的名字,基于这个原因,python设计了super()机制

    super是一个什么玩意?首先看官方的定义

    super() 函数是用于调用父类(超类)的一个方法。

    super()语法
    super(type[, object-or-type])
    
    • 1
    你一定会发现很多这样super的写法
    class Person(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    class Male(Person):
        def __init__(self, name, age, height):
            super().__init__(name, age)
            self.height = height
    
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 为什么super多用在__init__函数呢? 原因很简单,python的构造函数不同于c++语言构造函数使用的是类名,它的构造函数统一采用__init__这个函数名,并且__init__也是我们平时最常用的魔法方法,当我们在__init__对子类的成员做初始化时,发现父类中已经实现这个初始化的操作了,我们就可以直接用调用父类的构造函数来对我们的属性做初始化。

    但是super()怎么就能实现从父类拿到方法呢?

    class Person(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    class Male(Person):
        def __init__(self, name, age, height):
            super().__init__(name, age)
            self.height = height
            print(super())
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
    >>>>>>>>>>>>>>>>>>>>>>>>>
    <super: <class 'Male'>, <Male object>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 注意观察以上代码,我们直接打印super(),可以发现super()返回的是, >,可以看到在子类中的构造函数打印出来发现,super()的第一个参数为class,第二个参数为object
    • python的黑魔法干了两件事:它会寻找super()是在哪个类定义的,然后把这个class放在super的第一个参数中,第二它会寻找自己是在哪个函数给定义的,然后把这个函数的第一个参数即self传到super的第二个参数中

    super()的完全体是什么?

    class Person(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    class Male(Person):
        def __init__(self, name, age, height):
            super(Male, self).__init__(name, age)
            self.height = height
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 通过super的语法定义并且查阅资料我还发现super可以这样写,这就解释通了刚刚上个例子打印出来的东西,原来我们是需要在super()里面先传一个子类的类名,再传一个子类的实例对象(self代表的是一个当前实例化的对象,可以在我的博客找到解释)

    那super()做了什么事情呢?

    class Person(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    class Male(Person):
        def __init__(self, name, age, height):
            super(Male, self).__init__(name, age)
            # Person().__init__(name, age)
            self.height = height
            print(Male.mro())
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
        
        
    >>>>>>>>>>>>>>>>>>>>>>>>>>>
    [<class '__main__.Male'>, <class '__main__.Person'>, <class 'object'>]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    我直接给出结论:我们首先需要在第二个参数self找到当前的mro(当前所处类的mro),从上面可以看到当前的mro为 Male->Person->object,并且从第一个参数的所处的mro往前找,即Male往前找就是Person,然后看到Person有没有这个__init__函数,如果有这个函数则将父类绑定到self,实现父类构造函数初始化子类属性。

    class Person(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    class Male(Person):
        def __init__(self, name, age, height):
            # super(Male, self).__init__(name, age)
            Person.__init__(self, name, age)
            self.height = height
    
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
        print(m.__dict__)
    
    
    {'name': 'xiaoming', 'age': 18, 'height': 172}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    class Person(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    class Male(Person):
        def __init__(self, name, age, height):
            super(Male, self).__init__(name, age)
            # Person.__init__(self, name, age)
            self.height = height
    
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
        print(m.__dict__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    上面两行代码都输出{'name': 'xiaoming', 'age': 18, 'height': 172},说明Person.__init__(self, name, age) == super(Male, self).__init__(name, age),实现的机制就是mro寻找

    接下来我们再探究一下super的mro机制

    class Animal(object):
        def __init__(self, name):
            self.name = name
    
    
    class Person(Animal):
        def __init__(self, name, age):
            super(Person, self).__init__(name)
            self.age = age
    
    class Male(Person):
        def __init__(self, name, age, height):
            super(Person, self).__init__(name)
            #super(Male, self).__init__(name, age)
            self.height = height
    
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
        print(m.__dict__)
        
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    {'name': 'xiaoming', 'height': 172}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 观察上面例子,我们定义了三个类,Person继承Amimal,Male继承Person,但是我们再Male的super()中的第一个参数传入Person,根据我们上述定义的super机制,super此时传入的第二个参数为self,self是Male实例化的对象,Male的mro为Male->Person->Animal,然后我们再第一个参数传入Person,此时super会从Person开始往上找到Animal,这样我们会用Animal的构造函数给Male这个类做初始化,直接跳过了Person这个类的构造函数
    class Animal(object):
        def __init__(self, name):
            self.name = name
    
    
    class Person(Animal):
        def __init__(self, name, age):
            super(Person, self).__init__(name)
            self.age = age
    
    class Male(Person):
        def __init__(self, name, age, height):
            # super(Person, self).__init__(name)
            #super(Male, self).__init__(name, age)
            self.height = height
    
    
    if __name__ == '__main__':
        m = Male('xiaoming', 18, 172)
        super(Male,m).__init__('xiaoming',118)
        print(m.__dict__)
        
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    {'height': 172, 'name': 'xiaoming', 'age': 118}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 明白了super的第一个参数和第二个参数的含义,我们甚至可以在类外使用super函数对子类进行初始化,传入类的名称和实例化的对象就行

    super的mro继承问题

    class Animal(object):
        def say(self):
            print("动物在叫")
    
    
    class Cat(Animal):
        def say(self):
            super(Cat, self).say()
    
    
    class Dog(Animal):
        def say(self):
            print("狗在叫")
            print(CatDog.mro())
    
    
    class CatDog(Cat, Dog):
        def say(self):
            super(CatDog, self).say()
    
    if __name__ == '__main__':
        cat_dog = CatDog()
        cat_dog.say()
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>
    狗在叫
    [<class '__main__.CatDog'>, <class '__main__.Cat'>, <class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 通过上述例子我们发现我们实例化了CatDog(),调用了say(),根据mro机制我们可能我们是先继承了Cat这个类,Cat又继承了Animal,那应该打印的应该是动物在叫,但是实际打印出来的是狗在叫,这是因为mro机制,当我们从Cat这个类寻找say方法时发现并没有say()方法,那么它会沿着这个mro链去找发现Dog这个类有这个say方法,则调用这个方法

    启示:避免在多继承中使用super

    总结:

    本文首先引出了继承中存在的问题如何在子类调用父类的同名函数,然后引出c++和python做法,python中super的常见用法,super()两个参数的含义,super在多继承出现的问题

    参考

    https://www.bilibili.com/video/BV1FL4y1E7xK
    https://www.geeksforgeeks.org/python-super/

  • 相关阅读:
    记录使用IDEA时出现的小问题
    【MHA】MySQL高可用MHA介绍3-命令详解
    MySQL表的创建、删除、修改、复制
    ros指令 rqt_bag <bagname>
    python项目(课设)——飞机大战小游戏项目源码(pygame)
    sprinboot打包jar后读取不到/resource/data/ip2region.xdb的文件.
    Android应用自启动保活手段与安全现状分析
    UE5制作场景时的小技巧和注意事项
    MATLAB程序设计与应用 3.4 矩阵的特征值与特征向量
    十二、stm32-红外遥控(OLED显示)
  • 原文地址:https://blog.csdn.net/weixin_46187354/article/details/126639065