Python中的类和对象(4)

文章介绍了Python编程中的构造函数(_init_)用于对象初始化,以及如何通过重写实现子类对父类方法的定制。讨论了钻石继承(菱形继承)可能导致的问题,如构造函数被多次调用,并通过super()函数和MRO(方法解析顺序)来解决这类问题,确保代码的正确执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 构造函数 (_ init _(self[,……]))

在类中定义 _init _() 方法,可以实现在实例化对象的时候进行个性化定制:

>>> class C:
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...     def add(self):
...         return self.x + self.y
...     def mul(self):
...         return self.x * self.y
...        
>>> c = C(2, 3)
>>> c.add()
5
>>> c.mul()
6

2.重写

前面我们在 “继承” 中讲过,如果对于父类的某个属性或方法不满意的话,完全可以重新写一个同名的属性或方法对其进行覆盖。那么这种行为,我们就称之为是子类对父类的重写。

这里,我们可以定义一个新的类 —— D,继承自上面的类 C,然后对 add() 和 mul() 方法进行重写:

>>> class D(C):
...     def __init__(self, x, y, z):
...         C.__init__(self, x, y)
...         self.z = z
...     def add(self):
...         return C.add(self) + self.z
...     def mul(self):
...         return C.mul(self) * self.z
...
>>> d = D(2, 3, 4)
>>> d.add()
9
>>> d.mul()
24

3.砖石继承

下面代码中,类 B1 和 类 B2 都是继承自同一个父类 A,而类 C 又同时继承自它们,这种继承模式就被称之为钻石继承,或者菱形继承:

>>> class A:
...     def __init__(self):
...         print("哈喽,我是A~")
...
>>> class B1(A):
...     def __init__(self):
...        A.__init__(self)
...        print("哈喽,我是B1~")
...
>>> class B2(A):
...     def __init__(self):
...         A.__init__(self)
...         print("哈喽,我是B2~")
...
>>> class C(B1, B2):
...     def __init__(self):
...         B1.__init__(self)
...         B2.__init__(self)
...         print("哈喽,我是C~")
...

钻石继承这种模式,一旦处理不好就容易带来问题:

>>> c = C()
哈喽,我是A~
哈喽,我是B1~
哈喽,我是A~
哈喽,我是B2~
哈喽,我是C~

看,“哈喽,我是A~” 这一句竟然打印了 2 次!

也就是说,类 A 的构造函数被调用了 2 次!

怎么解?

看下面~

4.supper()函数和MRO顺序

上面这种通过类名直接访问的做法,是有一个名字的,叫 “调用未绑定的父类方法”。

通常使用其实没有多大问题,但是遇到钻石继承嘛,就容易出事儿了~

那么其实 Python 还有一个更好的实现方案,就是使用 super() 函数。

super() 函数能够在父类中搜索指定的方法,并自动绑定好 self 参数。

>>> class B1(A):
...     def __init__(self):
...        super().__init__()
...        print("哈喽,我是B1~")
...
>>> class B2(A):
...     def __init__(self):
...         super().__init__()
...         print("哈喽,我是B2~")
...
>>> class C(B1, B2):
...     def __init__(self):
...         super().__init__()
...         print("哈喽,我是C~")
...
>>> c = C()
哈喽,我是A~
哈喽,我是B2~
哈喽,我是B1~
哈喽,我是C~

之所以 super() 函数能够有效避免钻石继承带来的问题,是因为它是按照 MRO 顺序去搜索方法,并且自动避免重复调用的问题。

那什么是 MRO 顺序呢?

MRO(Method Resolution Order),翻译过来就是 “方法解析顺序”。

想要查找一个类的 MRO 顺序有两种方法~

一种是通过调用类的 mro() 方法:

>>> C.mro()
[<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>]
>>> B1.mro()
[<class '__main__.B1'>, <class '__main__.A'>, <class 'object'>]

另一种则是通过 mro 属性:

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A'>, <class 'object'>)
>>> B2.__mro__
(<class '__main__.B2'>, <class '__main__.A'>, <class 'object'>)

注:这里大家会看到它们都有一个 <class ‘object’>,这是因为 object 是所有类的基类,所以就算你不写,它也是会被隐式地继承。
supper()函数同时存在一些问题,顺序不确定

5.思维导图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰深入学习计算机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值