​既是爸爸又是爷爷?Python 多继承中的一个诡异现象

简介: ​既是爸爸又是爷爷?Python 多继承中的一个诡异现象

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


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)


运行效果如下图所示:


1.png


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


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


2.png


我们也知道,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()


运行效果如下图所示:


3.png


大家仔细观察,会发现这段代码有点奇怪。我调用的是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


运行效果如下图所示:


4.png


从图里面可以看到,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)


5.png



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

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


6.png


大家注意画红线的地方,self始终都是Son类的实例。所以,一开始初始化.address的时候,就是初始化的Son的实例的.address属性。后面在.where里面调用.address的时候,也是读取的Son的实例的.address属性。


所以,并不存在Father类去读GrandFather类的情况。自始至终,都是Son类的实例在进行各种操作。


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


7.png


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


请关注微信公众号【未闻Code】获取更多精彩文章。

目录
相关文章
|
21天前
|
Python
Python中的继承:概念、用法与示例
Python中的继承:概念、用法与示例
28 0
|
21天前
|
Python
请简述Python中的继承、封装和多态的概念。
【2月更文挑战第24天】【2月更文挑战第82篇】请简述Python中的继承、封装和多态的概念。
|
21天前
|
Python
Python 面向对象编程: 在 Python 中如何实现继承?
Python 面向对象编程: 在 Python 中如何实现继承?
46 0
|
21天前
|
Python
Python 面向对象编程:什么是面向对象编程(OOP)?解释封装、继承和多态的概念。
Python 面向对象编程:什么是面向对象编程(OOP)?解释封装、继承和多态的概念。
44 0
|
21天前
|
Python
Python中的面向对象编程与继承
本文将深入探讨Python中面向对象编程的核心概念,重点讨论继承的实现原理以及在实际开发中的应用。通过详细的示例和解释,读者将能够全面理解Python中继承的使用方式和优势,为提高代码的复用性和可维护性提供有效的技术支持。
|
15天前
|
Python
Python 继承
Python 继承
9 0
|
15天前
|
Python
Python进阶---面向对象 继承
Python进阶---面向对象 继承
10 1
|
21天前
|
Python
python面向对象编程,解释继承和多态的概念。
python面向对象编程,解释继承和多态的概念。
|
21天前
|
Python
Python从入门到精通:深入学习面向对象编程——2.1.2继承、封装和多态的概念
Python从入门到精通:深入学习面向对象编程——2.1.2继承、封装和多态的概念
|
21天前
|
数据库 Python
python面型对象编程进阶(继承、多态、私有化、异常捕获、类属性和类方法)(下)
python面型对象编程进阶(继承、多态、私有化、异常捕获、类属性和类方法)
51 0