Python 多继承中的一个诡异现象 既是 Father又是grandfather

作者:itskingname 时间:2023-02-06 10:24:34 

我们知道,在面向对象编程里面, 继承 是一个很重要的概念。子类可以使用父类的方法和属性。

例如下面这段代码:


class Father:
   def __init__(self):
       self.address = '上海'

def say(self):
       print('我是爸爸')

class Son(Father):
   def __init__(self):
       super().__init__()

def say(self):
       print('我是儿子')

son = Son()
print(son.address)

运行效果如下图所示:

Python 多继承中的一个诡异现象 既是 Father又是grandfather

从图中可以看到,子类并没有 self.address 这个属性,但是当我们直接打印的时候,并不会报错,它会自动使用父类的 address 属性。

显然,如果一个属性,子类也没有,父类也没有,那肯定会报错,如下图所示:

Python 多继承中的一个诡异现象 既是 Father又是grandfather

我们也知道,Python 是支持多继承的,一个子类可以有多个父类。那么,大家请看下面这段代码:


class GrandFather:
   def __init__(self):
       self.address = '上海'

def say(self):
       print('我是爸爸')

class Father:
   def __init__(self):
       self.age = 100

def where(self):
       print('我现在住在:', self.address)

class Son(GrandFather, Father):
   def __init__(self):
       super().__init__()

def say(self):
       print('我是儿子')

son = Son()
son.where()

运行效果如下图所示:

Python 多继承中的一个诡异现象 既是 Father又是grandfather

大家仔细观察,会发现这段代码有点奇怪。我调用的是 son.where() 方法,由于 Son 类没有这个方法,于是它会去它的两个父类里面找。于是在 Father 这个父类里面找到了。于是执行 Father 里面的 where() 方法,目前为止没有问题。

但接下来就不对了, .where() 方法里面,调用了 self.address 属性。可问题是 Father 这个类它并没有 .address 属性啊!而且 Father 也没有父类,那么这个 .address 属性是从哪里来的?

难道说,在开发者不知道的隐秘的角落里面, GrandFather 类悄悄成为了 Father 的父类?这样一来, GrandFather 岂不是又是 C 的父类,又是 C 的父类的父类? GrandFather 既是爸爸又是爷爷?

实际上,并不存在这么混乱的关系。要解释这个现象,我们就要从 self 这个东西说起。

我们知道,类的属性都是以 self 开头,方法的第一个参数也是 self 。那么这个  self 到底是什么东西?我们用一段小代码来看看它是什么东西:


class A:
   def get_self(self):
       return self

test = A()
what_is_self = test.get_self()

test is what_is_self

运行效果如下图所示:

Python 多继承中的一个诡异现象 既是 Father又是grandfather

从图里面可以看到, self 实际上就是这个类的实例。我们再来看有继承的情况:


class A:
   def get_self(self):
       return self

class B(A):
   def __init__(self):
       ...

test = B()
what_is_self = test.get_self()

print(what_is_self)

Python 多继承中的一个诡异现象 既是 Father又是grandfather

从图中可以看到,虽然我在 A 类的 .get_self() 方法中返回了 self ,但这个 self 实际上是 B 类的实例。因为我自始至终就只初始化了 B 类,并没有初始化 A 类。A 虽然是 B 类的父类。但父类的  self 都会变成子类的实例。

明白这一点以后,前面的问题就很好解释了,我们多打印一些信息:

Python 多继承中的一个诡异现象 既是 Father又是grandfather

大家注意画红线的地方, self 始终都是 Son 类的实例。所以,一开始初始化 .address 的时候,就是初始化的 Son 的实例的 .address 属性。后面在 .where 里面调用 .address 的时候,也是读取的 Son 的实例的 .address 属性。所以,并不存在 Father 类去读 GrandFather 类的情况。自始至终,都是 Son 类的实例在进行各种操作。

所以,在这个例子里面,当使用了继承以后,所有父类的属性和方法,子类如果有相同的名字,那么以子类的为准。如果子类没有定义,那么父类的属性和方法,其实都会跑到子类里面去。 所有看起来是父类进行的操作,其实都是子类在进行 。上面的代码,甚至可以近似等价于:

Python 多继承中的一个诡异现象 既是 Father又是grandfather

由于 say 方法在子类中有了定义,所以子类覆盖父类。以子类的 say 方法为准。 where address 由于子类没有定义,所以 Father 类的 where 方法和 GrandFather 里面的 address 属性,都会直接 跑 到子类里面。

来源:https://www.tuicool.com/articles/iIZRjuM

标签:Python,多继承
0
投稿

猜你喜欢

  • Python实现绘制多角星实例

    2023-08-26 13:42:14
  • 运用Python快速的对MySQL数据库进行重命名

    2024-01-17 22:36:25
  • JavaScript 数据结构之字典方法

    2024-04-16 09:28:22
  • Python常用数据结构和公共方法技巧总结

    2021-10-18 06:02:01
  • 使用Python和GDAL给图片加坐标系的实现思路(坐标投影转换)

    2021-11-10 03:15:31
  • 不同浏览器的兼容一些写法

    2009-03-26 12:58:00
  • 清空所有表中的数据的存储过程

    2024-01-16 11:00:08
  • Python 实现「食行生鲜」签到领积分功能

    2023-02-25 16:26:00
  • python计算列表元素与乘积详情

    2023-05-12 00:50:19
  • 利用Pandas和Numpy按时间戳将数据以Groupby方式分组

    2022-01-04 01:47:44
  • js判断传入时间和当前时间大小实例(超简单)

    2024-05-02 17:26:40
  • python删除列表元素del,pop(),remove()及clear()

    2023-10-07 14:51:35
  • Python中的流程控制详解

    2023-07-22 20:31:54
  • Python之lambda匿名函数及map和filter的用法

    2021-01-14 02:11:18
  • python深度学习之多标签分类器及pytorch实现源码

    2022-09-26 01:09:12
  • Python通过psd-tools解析PSD文件

    2023-05-25 12:08:47
  • Python Mysql自动备份脚本

    2024-01-16 00:57:46
  • 用ASP+CSS实现随机背景

    2007-09-26 12:33:00
  • 通过Python 获取Android设备信息的轻量级框架

    2021-08-18 21:41:00
  • Python 列表list使用介绍

    2021-01-03 09:37:16
  • asp之家 网络编程 m.aspxhome.com