当前位置: 首页 > news >正文

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;
}
  • 我们会发现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()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
猫吃东西
动物吃东西
  • 根据c++的思想,我们可以很容易的想到直接在子类使用父类名来调用这个方法,但是你在将来维护代码的时候会发现,当我们想改父类的名字的时候,我们会发现我们需要在子类也改父类的名字,基于这个原因,python设计了super()机制

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

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

super()语法
super(type[, object-or-type])
你一定会发现很多这样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)
  • 为什么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>>
  • 注意观察以上代码,我们直接打印super(),可以发现super()返回的是<super: <class 'Male'>, <Male object>>,可以看到在子类中的构造函数打印出来发现,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)
  • 通过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'>]

我直接给出结论:我们首先需要在第二个参数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}
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},说明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}
  • 观察上面例子,我们定义了三个类,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}
  • 明白了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'>]
  • 通过上述例子我们发现我们实例化了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/

相关文章:

  • Redis 的持久化
  • 2022年中国证券行业智能投顾专题分析
  • MYSQL高可用架构之MHA实战(真实可用)
  • 【Reinforcement Learning】蒙特卡洛算法
  • SAP ABAP ADT安装说明 as 20220901
  • 计算机组成原理知识总结(八)输入/输出系统
  • springboot基于java的康泰小区物业管理系统的设计与实现毕业设计源码101926
  • java查看对象真实地址和哈希值的工具类
  • SOLIDWORKS直播课:解锁3DE协同设计平台的“云端结构设计角色”
  • 简单的 手写 服务器
  • 01 RocketMQ - NameServer 源码分析
  • 【CSS】数据面板
  • 记一次盖茨木马应急响应
  • 兔老大的系统设计(二)定时系统(延时队列)
  • STM32cubeMX详细教学及多个小项目合集(包含RTOS)
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • 【css3】浏览器内核及其兼容性
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • C++11: atomic 头文件
  • classpath对获取配置文件的影响
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • mac修复ab及siege安装
  • MobX
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • SQLServer之创建数据库快照
  • 对象引论
  • 利用jquery编写加法运算验证码
  • 聊聊flink的TableFactory
  • 运行时添加log4j2的appender
  • 阿里云服务器购买完整流程
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • ​你们这样子,耽误我的工作进度怎么办?
  • #NOIP 2014#Day.2 T3 解方程
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (排序详解之 堆排序)
  • (算法)Travel Information Center
  • (转)C语言家族扩展收藏 (转)C语言家族扩展
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET delegate 委托 、 Event 事件
  • .NET Framework杂记
  • .Net MVC4 上传大文件,并保存表单
  • .NET 表达式计算:Expression Evaluator
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .net 托管代码与非托管代码
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .net快速开发框架源码分享
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • @RequestBody详解:用于获取请求体中的Json格式参数