认识python中的super函数
需求分析
在类继承中,存在这么一种情况:
class Human(object):
def Move(self):
print("我会走路...")
class Man(Human):
def Move(self):
print("我会跑步...")
man().Move()
输出:
我会跑步...
人先会走路再会跑步的。如果你想调用Man().Move()
时输出以下要怎么做?
我会走路...
我会跑步...
这时,其实便需要调用父类Human
的Move()方法了。我们知道,在子类中,会覆父类的同名方法。即子类Man
里的Move()
方法将父类Human
中的同名方法Move()
覆盖了。要想同时使得两个方法都奏效,可以在子类中主动调用父类中的Move()方法。其实很简单,在类Man中加点东西就行了,如下:
class Human(object):
def Move(self):
print("我会走路...")
class Man(Human):
def Move(self):
Human.Move(self)
print("我会跑步...")
Man().Move()
output:
我会走路...
我会跑步...
代码中的Human.Move(self)
便是在子类Man中主动跟调用父类的方法。我来解释一下这句代码:
- Human.Move(self)中Human是类,由类直接调用Move方法,这是未绑定的调用。
- Human.Move(self)中的self其实是Man的一个实例Man(),一个个类的实例化是这样的
m=Man()
,其中,m和Man()都是类Man的实例。
但是,这种方法有一个弊端。比如,如果父类Human
吃饱没事干给自己换个名字叫GoodHuman
,那么麻烦就来了,你不仅要把Man(Human)
改为Man(GoodHuman)
,还要把Human.Move(self)
改为GoodHuman.Move(self)
。
当然,这里的例子改起来是没什么可怕。但万一在一些项目里像Human.Move(self)
这类型的方法很多的话,你要一个一个改?
有没有什么办法,使得在父类更改名称时,子类只要改类似Man(Human)
一处就好?
super()出场
super()的作用是:在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现。
调用super()地常用格式是:
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
现在,只要将代码改为以下,就不怕父类名称地变化了。(输出结果和上述一样)
class Human(object):
def Move(self):
print("我会走路...")
class Man(Human):
def Move(self):
super(Man, self).Move()
print("我会跑步...")
稍微深入
上面地例子很简单,因为类Man继承地父类已有一个。当继承关系变复杂时,(比如同时继承很多个类,继承的类又继承其他类等等...),便要知道super()是怎么处理继承顺序了。
看这个例子:
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
class A(Base):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
按照前面的思路。我们意淫的输出应该是:
enter C
enter A
enter Base
leave Base
leave A
enter B
enter Base
leave Base
leave B
leave C
但实际输出却是:
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C
我在这里提一个疑问:
**enter A
的下一句为什么不是enter Base
而是enter B
么? **
揭开super的面纱
其实,对于你定义的每一个类,Python 会用方法解析顺序(Method Resolution Order, MRO)计算出一个列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表:
>>>print(C.mro())
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
[Finished in 0.2s]
一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则:
- 子类永远在父类前面;
- 如果有多个父类,会根据它们在列表中的顺序被检查;
- 如果对下一个类存在两个合法的选择,选择第一个父类.
我们查看super函数的代码:
ef super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
其中,cls 代表类,inst 代表实例,上面的代码做了两件事:
- 获取 inst 的 MRO 列表;
- 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]。
当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。
现在我们来通过一步步追踪来回答上面提出的答案:
做法很简单:
1, 修改一下代码:
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
print(self)
class A(Base):
def __init__(self):
print("enter A")
print(self)
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
print(self)
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
print(self)
super(C, self).__init__()
print("leave C")
c = C()
print(hex(id(c)))
看输出:
enter C
<__main__.C object at 0x000001C614CAB7F0>
enter A
<__main__.C object at 0x000001C614CAB7F0>
enter B
<__main__.C object at 0x000001C614CAB7F0>
enter Base
leave Base
<__main__.C object at 0x000001C614CAB7F0>
leave B
leave A
leave C
0x1c614cab7f0
在这里塞了这么多代码,就为了说明两件事:
- 在A,B,C,Base这四个类中,self都是
<__main__.C object at 0x000001C614CAB7F0>
这个对象。 <__main__.C object at 0x000001C614CAB7F0>
是类C的实例c(c的id输出是0x1c614cab7f0
,说明它们是同样的东西)。实例c也就是整个过程的发起者。
到了这里,就可以回答问题了:
首先看类C的__init__方法:
super(C, self).__init__()
这里的 self 是当前 C 的实例,self.__class__.mro()
结果是:
[__main__.C, __main__.A, __main__.B, __main__.Base, object]
可以看到,C 的下一个类是 A,于是,跳到了 A 的 __init__
,这时会打印出 enter A,并执行下面一行代码:
super(A, self).__init__()
注意,这里的 self 也是当前 C 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的__init__
,这时会打印出enter B,而不是enter Base。
总结
super和父类没有实质性的关联。通过上面分析。我们知道super函数是根据self
以及该self对应的方法解析顺序mro
来工作的。
再强调一下,self是一个实例对象,在这里就是实例c(上面代码有一句c=C()),整个过程的发起者。
版权:保留所有权,转载请注明出处!
认识python中的super函数的更多相关文章
- Python中的super函数,你熟吗?
摘要:经常有朋友问,学 Python 面向对象时,翻阅别人代码,会发现一个 super() 函数,那这个函数的作用到底是什么? 本文分享自华为云社区<Python中的super函数怎么学,怎么解 ...
- python中的super( test, self).__init__()
python中的super( test, self).__init__() 对继承自父类的属性进行初始化 首先找到test的父类(比如是类A),然后把类test的对象self转换为类A的对象,然后“被 ...
- python中的super是什么?
技术背景 python中的super,名为超类,可以简单的理解为执行父类的__init__函数.由于在python中不论是一对一的继承,还是一子类继承多个父类,都会涉及到执行的先后顺序的问题.那么本文 ...
- python中的super()是什么?
技术场景:python中的super,名为超类,可以简单的理解为执行父类的__init__函数.由于在python中不论是一对一的继承,还是一子类继承多个父类,都会涉及到执行的先后顺序的问题.那么本文 ...
- python --- Python中的callable 函数
python --- Python中的callable 函数 转自: http://archive.cnblogs.com/a/1798319/ Python中的callable 函数 callabl ...
- python中使用zip函数出现<zip object at 0x02A9E418>
在Python中使用zip函数,出现<zip object at 0x02A9E418>错误的原因是,你是用的是python2点多的版本,python3.0对python做了改动 zip方 ...
- [转载]python中multiprocessing.pool函数介绍
原文地址:http://blog.sina.com.cn/s/blog_5fa432b40101kwpi.html 作者:龙峰 摘自:http://hi.baidu.com/xjtukanif/blo ...
- Python 中的isinstance函数
解释: Python 中的isinstance函数,isinstance是Python中的一个内建函数 语法: isinstance(object, classinfo) 如果参数object是cla ...
- Python中的map()函数和reduce()函数的用法
Python中的map()函数和reduce()函数的用法 这篇文章主要介绍了Python中的map()函数和reduce()函数的用法,代码基于Python2.x版本,需要的朋友可以参考下 Py ...
随机推荐
- 【PAT甲级】1048 Find Coins (25 分)(二分)
题意: 输入两个正整数N和M(N<=10000,M<=1000),然后输入N个正整数(<=500),输出两个数字和恰好等于M的两个数(小的数字尽可能小且输出在前),如果没有输出&qu ...
- vue 组件,以及组件的复用
有时候代码的某一模块可能会经常使用到,那么完全可以把这一模块抽取出来,封装为一个组件,哪里需要用到的时候只需把模块调用即可 .参考vue官方 https://cn.vuejs.org/v2/guide ...
- pexpect &&pxssh
python 3.6 pip install pexpect #!/usr/bin/python3 import os import sys curPath = os.path.abspath(os ...
- java 操作 csv文件
CSV是逗号分隔文件(Comma Separated Values)的首字母英文缩写,是一种用来存储数据的纯文本格式,通常用于电子表格或数据库软件.在 CSV文件中,数据“栏”以逗号分隔,可允许程序通 ...
- 字符流---Day32
时隔多久,我又回来写博客了,最近忙于两个课设,五周,搞得头发都不知道掉了多少根了,还没成为程序员就开始掉了,等我成为一名程序员的时候岂不是要秃头了,IT界的人会不会帮我当成大佬了,哈哈哈哈,希望我以后 ...
- MySQL8.0 ROW_NUMBER、RANK、DENSE_RANK窗口函数 分组排序排名
MySQL8.0 (ROW_NUMBER)窗口函数 排名 暂时理解函数意义,后面再进行优化,如果有关变量排序,查看这个大哥的 mysql的分组排序和变量赋值顺序 先查看一个例子: # 按照每科课程分数 ...
- 多项式输出 (0)<P2009_1>
多项式输出 (poly.pas/c/cpp) [问题描述] 一元n次多项式可用如下的表达式表示: 其中,称为i次项,ai称为i次项的系数.给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输 ...
- gitlab相关命令操作
[root@xuegod63 ~]# git config --global user.name "zsl3"[root@xuegod63 ~]# git config --glo ...
- VIM学习笔记一
键位图 转自:链接 永久显示行号: vi ~/.vimrc 加入 :set number 命令 简单说明 :w 保存编辑后的文件内容,但不退出vim编辑器.这个命令的作用是把内存缓冲区中的数据写到启动 ...
- 第二天python
1.pycharm的安装: 1.先去官网下载软件:https://www.jetbrains.com/pycharm/download/#section=windows然后进行下一步,下一步操作既可以 ...