python方法解析顺序_浅谈Python的方法解析顺序(MRO)
方法解析顺序, Method Resolution Order
从一段代码开始
考虑下面的情况:
class A(object):
def foo(self):
print('A.foo()')
class B(object):
def foo(self):
print('B.foo()')
class C(B, A):
pass
c = C()
c.foo()
C同时继承了类A和类B, 它们都有各自的foo()方法. 那么C的实例c调用foo()方法时, 到底是调用A.foo()还是B.foo()?
__mro__
Python的每一个有父类的类都有一个与方法解析顺序相关的特殊属性:__mro__, 它是一个tuple, 装着方法解析时的对象查找顺序: 越靠前的优先级越高. 执行下面的代码:
print type(C.__mro__)
print C.__mro__
输出:
(, , , )
可以看到, B在C的前面, 所以在上一段代码中, c.foo()调用的是B.foo()而不是A.foo().
之所以B在C的前面, 是因为在指定C的父类时先指定了B:
class C(B, A):
若将它改成:
class C(A, B):
c.foo()执行的就是A.foo()了.
熟悉环境变量的可以将__mro__理解为以目标对象为环境的PATH变量: 从左到右开始查找, 找到就执行, 然后返回结果.
方法解析顺序
从C.__mro__的值可以看出, Python的方法解析优先级从高到低为:
1. 实例本身(instance)
2. 类(class)
3. super class, 继承关系越近, 越先定义, 优先级越高.
其实属性解析顺序也基本一致, 只不过多了个__getattr__的查找(见Python对象的属性访问过程).
补充知识:python中的单继承,多继承和mro顺序
python作为一门动态语言,是和c++一样支持面向对象编程的。相对对象编程有三大特性,分别是继承,封装和多态。今天我们重点讲解的是,python语言中的单继承和多继承。
继承概念:
如果一个类继承了另外一个类时,它将自动获得另一个类的所有属性和方法,那么原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法。同时还可以定义自己的属性和方法。
单继承就是一个子类只能继承一个父类。
格式: class 子类(父类)
举例: class A(B)
A类拥有了B类的所有的特征,A类继承了B类
B类 父类,基类
A类 子类 派生类 后代类
继承的作用:功能的升级和扩展
功能的升级就是对原有 的功能进行完善重新,功能的扩展就是对原本没有的功能进行添加。减少代码的冗余。
下面我们举一个单继承的例子:
class Dog(): #父类
def __init__(self): #父类的属性初始化
self.name='狗'
self.leg=4
def __str__(self):
return "名字:%s %d 条腿"%(self.name,self.leg)
class Taidi(Dog): #定义一个Taidi 泰迪 类继承自Dog类 -->单继承
pass
taidi=Taidi()
print(taidi) 输出结果--> 名字:狗 4 条腿
多继承:
多继承就是一个子类同时继承自多个父类,又称菱形继承、钻石继承。
首先,我们先讲多继承中一个常见方法,单独调用父类的方法。在子类初始化的时候需要手动调用父类的初始化方法进行父类的属性的构造,不然就不能使用提供的属性。
在子类中调用父类的初始化方法格式就是: 父类名._init_(self)
下面举一个单独调用父类方法的例子:
print("******多继承使用类名.__init__ 发生的状态******")
class Parent(object): #父类
def __init__(self, name):
print('parent的init开始被调用')
self.name = name #属性的初始化
print('parent的init结束被调用')
class Son1(Parent): #单继承 Son1子类继承父类
def init(self, name, age):
print('Son1的init开始被调用')
self.age = age
Parent.init(self, name) #单独调用父类的属性
print('Son1的init结束被调用')
class Son2(Parent): #也是单继承 Son2继承父类
def init(self, name, gender):
print('Son2的init开始被调用')
self.gender = gender #单独调用父类的初始化属性方法
Parent.init(self, name)
print('Son2的init结束被调用')
class Grandson(Son1, Son2): #多继承,继承两个父类
def init(self, name, age, gender):
print('Grandson的init开始被调用')
Son1.init(self, name, age) # 单独调用父类的初始化方法
Son2.init(self, name, gender)
print('Grandson的init结束被调用')
gs = Grandson('grandson', 18, '男') #实例化对象
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("多继承使用类名.init 发生的状态\n\n")
下面让我们看看运行的结果:
******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 18
性别: 男
******多继承使用类名.__init__ 发生的状态******
mro顺序
查看上面的运行结果,我们发现由于多继承情况,parent类被的属性被构造了两次,如果在更加复杂的结构下可能更加严重。
为了解决这个问题,Python官方采用了一个算法将复杂结构上所有的类全部都映射到一个线性顺序上,而根据这个顺序就能够保证所有的类都会被构造一次。这个顺序就是MRO顺序。
格式:
类名._mro_()
类名.mro()
多继承中super调用有所父类的被重写的方法
super本质上就是使用MRO这个顺序去调用 当前类在MRO顺序中下一个类。 super().init()则调用了下一个类的初始化方法进行构造。
print("******多继承使用super().__init__ 发生的状态******")
class Parent(object):
def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def init(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init开始被调用')
self.age = age
super().init(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init结束被调用')
class Son2(Parent):
def init(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init开始被调用')
self.gender = gender
super().init(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init结束被调用')
class Grandson(Son1, Son2):
def init(self, name, age, gender):
print('Grandson的init开始被调用')
多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
super(Grandson, self).init(name, age, gender)
super().init(name, age, gender)
print('Grandson的init结束被调用')
print(Grandson.mro)
gs = Grandson('grandson', 18, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("多继承使用super().init 发生的状态\n\n")
查看下运行结果:
******多继承使用super().__init__ 发生的状态******
(, , , , )
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 18
性别: 男
******多继承使用super().__init__ 发生的状态******
单继承中super
print("******单继承使用super().__init__ 发生的状态******")
class Parent(object):
def __init__(self, name):
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def init(self, name, age):
print('Son1的init开始被调用')
self.age = age
super().init(name) # 单继承不能提供全部参数
print('Son1的init结束被调用')
class Grandson(Son1):
def init(self, name, age, gender):
print('Grandson的init开始被调用')
super().init(name, age) # 单继承不能提供全部参数
print('Grandson的init结束被调用')
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("单继承使用super().init 发生的状态\n\n")
运行结果:
******单继承使用super().__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
******单继承使用super().__init__ 发生的状态******
下面让我们总结下:
MRO保证了多继承情况 每个类只出现一次
super().__init__相对于类名.init,在单继承上用法基本无差
但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次
多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍,而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
下面是一个简答的面试题:
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)
运行结果:
1 1 1
1 2 1
3 2 3
以上这篇浅谈Python的方法解析顺序(MRO)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持菜鸟教程www.piaodoo.com。
python方法解析顺序_浅谈Python的方法解析顺序(MRO)相关推荐
- python sys模块作用_浅谈Python中的模块
模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在Python中,一个.py文件就称之为一个模块(Mod ...
- python中文字符串编码_浅谈python下含中文字符串正则表达式的编码问题
前言 Python文件默认的编码格式是ascii ,无法识别汉字,因为ascii码中没有中文. 所以py文件中要写中文字符时,一般在开头加 # -*- coding: utf-8 -*- 或者 #co ...
- python 共享内存变量_浅谈python多进程共享变量Value的使用tips
前言: 在使用tornado的多进程时,需要多个进程共享一个状态变量,于是考虑使用multiprocessing.Value(对于该变量的具体细节请查阅相关资料).在根据网上资料使用Value时,由于 ...
- python中怎么计数_浅谈python中统计计数的几种方法和Counter详解
1) 使用字典dict() 循环遍历出一个可迭代对象中的元素,如果字典没有该元素,那么就让该元素作为字典的键,并将该键赋值为1,如果存在就将该元素对应的值加1. lists = ['a','a','b ...
- python列表使用判断_浅谈Python数据类型判断及列表脚本操作
数据类型判断 在python(版本3.0以上)使用变量,并进行值比较时.有时候会出现以下错误: TypeError: unorderable types: NoneType() < int() ...
- 如何用python抓取文献_浅谈Python爬虫技术的网页数据抓取与分析
浅谈 Python 爬虫技术的网页数据抓取与分析 吴永聪 [期刊名称] <计算机时代> [年 ( 卷 ), 期] 2019(000)008 [摘要] 近年来 , 随着互联网的发展 , 如何 ...
- python数据类型转换原因_浅谈Python数据类型之间的转换
Python数据类型之间的转换 函数 描述 int(x [,base]) 将x转换为一个整数 long(x [,base] ) 将x转换为一个长整数 float(x) 将x转换到一个浮点数 compl ...
- python采用函数编程模式_浅谈Python 函数式编程
匿名函数lambda表达式 什么是匿名函数? 匿名函数,顾名思义就是没有名字的函数,在程序中不用使用 def 进行定义,可以直接使用 lambda 关键字编写简单的代码逻辑.lambda 本质上是一个 ...
- python打开方式错误_浅谈python 调用open()打开文件时路径出错的原因
昨晚搞鼓了一下python的open()打开文件 代码如下 def main(): infile =open("C:\Users\Spirit\Desktop\bc.txt",'r ...
最新文章
- 2013汇总计算 广联达gcl_完整工程量计算书分享:广联达+EXCEL计算表+电子+工程模板等赠送...
- zepto+html5+php实现h5上传头像(移动端)上
- 330+ 个机器学习模型/库探索工具!Papers With Code 重磅推出!
- Spring是如何校验XML的
- 使用Axure RP原型设计实践05,了解公式
- 窥探Swift编程之强大的Switch
- 技术人生,专家本色——采访张善友老师后的一点感受
- java登录界面命令_Java命令行界面(第12部分):CLAJR
- 蓝桥杯第六届省赛JAVA真题----循环节长度
- python 简单trace 过滤处理
- .Net Framework4.5中Asp.net mvc使用Singal R轮训实现导入进度条功能
- find命令使用方法
- IdentityServer4 And AspNetCore.Identity Get AccessToken 问题
- W3C-Web Service
- .NET多线程编程(2)——Thread类
- c#oracle数组超出,急问:System.IndexOutOfRangeException: 索引超出了数组界限解决方案
- 诗词格律[4] 古体诗
- 使用 python 创建更改 word 文档
- DNS异步请求池原理与实现
- android 语言的设置与获取
热门文章
- requests 200 scrapy超时_selenium咋和scrapy一起用捏?
- python中列表字典元组之间的区别_python之字典、元组、列表的区别
- python编程一球从100米_Python练习题 015:一颗自由落地的球
- java app退出登录_java – 通过从一个Activity调用一个函数,将退出按钮添加到Android App...
- Jmeter BeanShell使用json.jar包处理Json数据
- 语言程序推箱子课设报告_“延期不延学”第13期 | C++篇 | c++课设建议
- mongodb数据合并设计_MongoDB:将来自多个集合的数据合并成一个
- 模拟人生畅玩版android,模拟人生畅玩版手机版
- 使用Arrays sort 方法進行排序
- 【BZOJ3227】红黑树,打表找规律/DP