方法解析顺序, 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__ 发生的状态****** ")

下面让我们看看运行的结果:

******多继承使用类名.__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__ 发生的状态****** ")

查看下运行结果:

******多继承使用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__ 发生的状态****** ")

运行结果:

******单继承使用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)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

python类中方法的执行顺序-浅谈Python的方法解析顺序(MRO)相关推荐

  1. python中怎么调用函数_浅谈Python中函数的定义及其调用方法

    一.函数的定义及其应用 所谓函数,就是把具有独立功能的代码块组织成为一个小模块,在需要的时候调用函数的使用包含两个步骤 1.定义函数–封装独立的功能 2.调用函数–享受封装的成果 函数的作用:在开发时 ...

  2. python中 是什么类型_浅谈python中的变量默认是什么类型

    浅谈python中的变量默认是什么类型 1.type(变量名),输出的结果就是变量的类型: 例如 >>> type(6) 2.在Python里面变量在声明时,不需要指定变量的类型,变 ...

  3. python的re2和re区别_浅谈Python中re.match()和re.search()的使用及区别

    1.re.match()fvk免费资源网 re.match()的概念是从头匹配一个符合规则的字符串,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None.fvk免费资源网 包含的参数如下: ...

  4. python中判断列表数据类型_浅谈Python数据类型判断及列表脚本操作

    数据类型判断 在python(版本3.0以上)使用变量,并进行值比较时.有时候会出现以下错误: TypeError: unorderable types: NoneType() < int() ...

  5. python中zip的使用_浅谈Python中的zip()与*zip()函数详解

    前言 1.实验环境: Python 3.6: 2.示例代码地址:下载示例: 3.本文中元素是指列表.元组.字典等集合类数据类型中的下一级项目(可能是单个元素或嵌套列表). zip(*iterables ...

  6. python中dtype什么意思_浅谈python 中的 type(), dtype(), astype()的区别

    如下所示: 函数 说明 type() 返回数据结构类型(list.dict.numpy.ndarray 等) dtype() 返回数据元素的数据类型(int.float等) 备注:1)由于 list. ...

  7. python中image什么意思_浅谈python图片处理Image和skimage的区别

    做cnn的难免要做大量的图片处理.由于接手项目时间不长,且是新项目,前段时间写代码都很赶,现在稍微总结(恩,总结是个好习惯). 1,首先安装python-Image和python-skimage.py ...

  8. python 读excel字符型 数值_浅谈python 读excel数值为浮点型的问题

    浅谈python 读excel数值为浮点型的问题 如下所示: #读入no data = xlrd.open_workbook("no.xlsx") #打开excel table = ...

  9. python老是报参数未定义_浅谈Python程序的错误:变量未定义

    Python程序的错误种类 Python程序的错误分两种.一种是语法错误(syntax error).这种错误是语句的书写不符合Python语言的语法规定.第二种是逻辑错误(logic error). ...

最新文章

  1. PYTHON自动化Day3-列表/元组/切片/字典/字符串处理方法
  2. 华为鸿蒙还会不会推出,华为如果把鸿蒙独立出来,让小米、魅族和蓝绿厂参股进来,会不会超越安卓?...
  3. 查询数据库中数据的年份_本地公开数据中的年份
  4. 随想录(英语学习的几个误区)
  5. 关于控件开发的几点意见
  6. php 串行化与json(转)--很不错的文章
  7. mysql如何修改字段名字_mysql如何修改字段名称
  8. 多显示器被禁用无法开启_高刷等带来丝滑游戏体验:华硕ROG XG32VC电竞显示器...
  9. 计算机音乐第二十首,二十首励志歌曲
  10. PHP中的符号 -、= 和 :: 详解
  11. 闲聊,从《斗罗大陆》到游戏开发
  12. python atm作业详解_Python小案例--ATM系统
  13. 关于DIN 5510-2德国轨道车辆防火测试标准
  14. 先人들의 白頭山 登程路
  15. 18章 资产收益率和风险
  16. 条款11:在operator=中处理“自我赋值”
  17. android app 获得root 权限管理,获得Android App的“root”权限
  18. 微信小程序之文档管理系统(含源码+论文+答辩PPT等)
  19. 将瞰景smart3d空三结果导入contextcapture(CC)进行建模
  20. 用python字典编通讯录算法_Python学习-字典练习:简单通讯录

热门文章

  1. 对 VR 项目开发流程的调研
  2. 13. python 类
  3. Linux内核学习笔记
  4. Openfire 代码部署报错: Variable references non-existent resource:${workspace_loc:openfire_src}...
  5. python学习官网-Python学习(一)—— 初识python
  6. python编程和plc哪个好-plc和python
  7. 2018python培训-Python学习之路—2018/7/2
  8. 快速记忆python函数-让Python程序快速提升30%的技巧
  9. c语言和python哪个自学好-有c语言基础 自学python 应该选什么书来看?
  10. python爬虫原理-python爬虫从入门到放弃(二)之爬虫的原理