Python总结(三)
本系列其他教程:
Python教程(一):基本语法、流程控制、数据容器
Python教程(二):函数、异常、模块&包、文件读取、常用模块
文章目录
- Python总结(三)
-
- 一、类&对象
-
- [1.1 成员方法](#1.1 成员方法)
- [1.2 构造方法](#1.2 构造方法)
- [1.3 类属性&类方法&静态方法](#1.3 类属性&类方法&静态方法)
- [1.4 魔术方法](#1.4 魔术方法)
- [1.5 私有成员](#1.5 私有成员)
- [1.6 继承](#1.6 继承)
-
- [1.6.1 单继承&多继承](#1.6.1 单继承&多继承)
- [1.6.2 复写](#1.6.2 复写)
- [1.6.3 调用父类同名属性](#1.6.3 调用父类同名属性)
- [1.6.4 多态和抽象](#1.6.4 多态和抽象)
- [1.7 自定义属性](#1.7 自定义属性)
- 二、类型注解
-
- [2.1 基本用法](#2.1 基本用法)
- [2.2 容器类型注解](#2.2 容器类型注解)
- [2.3 type定义类型注解](#2.3 type定义类型注解)
- [2.4 Union联合类型](#2.4 Union联合类型)
- 三、闭包
- 四、装饰器
- 五、构造方法重写补充
-
- [5.1 子类单继承没有定义构造方法](#5.1 子类单继承没有定义构造方法)
- [5.2 子类单继承重写构造方法](#5.2 子类单继承重写构造方法)
- [5.3 MRO](#5.3 MRO)
-
- [5.3.1 子类多继承没有定义构造方法](#5.3.1 子类多继承没有定义构造方法)
- [5.3.2 子类多继承重写构造方法](#5.3.2 子类多继承重写构造方法)
一、类&对象
使用 class
关键字来定义类,通过 对象=类名称()
来创建对象。
- 对象也像变量一样,不用声明类型。
- 创建对象的写法就像调用 "类函数" 一样。
1.1 成员方法
- 类中定义的成员方法必须要有一个形参
self
,且必须放在所有参数的第一位。- self 表示类对象自身的意思,在成员方法内想要访问成员变量必须使用 self 来访问。
- 使用类对象调用成员方法的时候,可以当 self 参数不存在。
- 当使用类对象调用成员方法时,self 会自动被 Python 传入。
python
# 定义类
class Student:
# 成员变量
name = None
# 成员方法,必须要有self参数
def myMethod(self, address):
# 通过self来访问成员变量
print(f"大家好,我叫{self.name},我的地址是{address}")
# 创建对象
s1 = Student()
# 定义成员变量值
s1.name = 'Jay'
# 调用成员方法(当作self参数不存在)
s1.myMethod('Taiwan') # 大家好,我叫Jay,我的地址是Taiwan
1.2 构造方法
- 构造方法在创建类对象的时候会自动调用。
- Python中构造方法的名称固定为
__init__()
,前后各两个下划线。 - 如果自己不定义构造方法,默认会有一个空参的构造方法;如果自己定义了构造方法,则默认的空参构造方法会失效。
- Python中构造方法的名称固定为
- 构造方法也属于成员方法,也必须有 self 参数。
- 经常用构造方法在创建对象的时候给成员变量赋值。
- 以下代码在类中并没有定义三个属性而构造方法直接使用了,下面会解释。
python
class Student:
# 构造方法
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def myInformation(self):
print(self.name, self.age, self.address)
# 创建对象自动调用构造方法,self当作不存在
stu1 = Student('Jay', 30, 'Taiwan')
stu1.myInformation() # Jay 30 Taiwan
1.3 类属性&类方法&静态方法
-
类属性:
- 对象是从类创造的,它们的属性和方法是各个对象独有的,互相不能共享。
- 而类也有属性和方法,且它们可以和类创建的所有对象共享,并且类本身不用创建对象也能访问。
- 以下代码定义了类属性,而不是对象属性:
- 访问类属性要使用类名,而不是self,self表示对象。
pythonclass Student: # 以下都是类属性 name = None age = None count = 0 def __init__(self, name, age): # 访问类属性要使用类名,而不是self,self表示对象 Student.count += 1 print(Student.count) # 0 stu1 = Student('Tom', 12) print(stu1.count) # 1 stu2 = Student('Jack', 18) print(stu2.count) # 2 print(Student.count) # 2 # 可以发现所有对象和类本身都共享类属性count
-
对象属性:
- 使用 self 定义在
__init__
构造方法中,每个对象独有。 - 以下代码定义了对象属性,而不是类属性:
- 访问对象属性要使用self,而不是类名。
pythonclass Student: def __init__(self, name, age, address): self.name = name self.age = age self.address = address
- 注意:定义了对象属性后一般不会再定义同名的类属性,这也是为什么第1.2节代码中没有定义类属性的原因。
- 使用 self 定义在
-
类方法:
- 类方法也不需要创建对象,通过类名就可以访问,通过对象也可以访问。
- 使用
@classmethod
装饰器定义,第一个参数是cls
,而不是self
,cls
表示类本身而不是对象本身,类方法中访问类中的其他类方法和类属性必须使用cls
。
pythonclass MyClass: # 类属性 class_attribute = 2 @classmethod def class_method(cls): # 类方法通过cls访问类属性 return cls.class_attribute # 调用类方法 print(MyClass.class_method()) # 2 # 也可以通过实例调用类方法,但效果相同 obj = MyClass() print(obj.class_method()) # 2
-
对象方法:
- 定义在类中,第一个参数是
self
,可以访问对象属性。
- 定义在类中,第一个参数是
-
静态方法:
- 静态方法通过
@staticmethod
装饰器在类中定义。 - 静态方法不使用
cls
或self
作为第一个参数。 - 静态方法类和对象都可以访问。
- 静态方法用于实现一些与类相关的功能,但在实现过程中不需要访问或修改类或对象的成员(不使用cls、self参数也没法访问),一般只是用来做与本类相关的一些逻辑运算。
pythonclass MyClass: @staticmethod def static_method(arg1, arg2): return arg1 + arg2 # 调用静态方法 result = MyClass.static_method(5, 10) print(result) # 输出: 15 # 也可以通过实例调用静态方法,但效果相同 obj = MyClass() result = obj.static_method(5, 10) print(result) # 输出: 15
- 静态方法通过
综合案例:
python
class Car:
# 类属性
wheels = 4
def __init__(self, brand, model, year):
# 对象属性
self.brand = brand
self.model = model
self.year = year
# 类方法
@classmethod
def is_motor_vehicle(cls):
return cls.wheels == 4
# 对象方法
def car_details(self):
return f"{self.brand} {self.model} ({self.year})"
# 静态方法
# 不访问类或对象中的任何成员
@staticmethod
def calculate_mileage(distance, fuel_used):
return distance / fuel_used
# 创建实例
car1 = Car("Toyota", "Camry", 2020)
car2 = Car("Honda", "Accord", 2019)
# 调用对象方法
print(car1.car_details()) # 输出: Toyota Camry (2020)
print(car2.car_details()) # 输出: Honda Accord (2019)
# 调用类方法
print(Car.is_motor_vehicle()) # 输出: True
# 调用静态方法
print(Car.calculate_mileage(500, 25)) # 输出: 20.0
# 访问类属性
print(Car.wheels) # 输出: 4
# 访问对象属性
print(car1.brand) # 输出: Toyota
print(car2.model) # 输出: Accord
1.4 魔术方法
Python内置的对象方法称为魔术方法(第一个参数是self):
__str__
- 用于控制输出对象的时候输出的内容,而不是输出没什么用的内存地址。
- 该方法必须返回一个字符串,这个返回的字符串就是打印对象时输出的内容。
python
# 没有定义__str__魔术方法
class Student1:
def __init__(self, name, age):
self.name = name
self.age = age
stu1 = Student1('Jack', 12)
print(stu1) # <__main__.Student object at 0x0000016A8BDE5810>
# 使用__str__魔术方法
class Student2:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'{self.name} {self.age}'
stu2 = Student2('Jack', 12)
# 输出魔术方法返回的内容
print(stu2) # Jack 12
__lt__
- 直接进行对象的比较是会报错的,该方法用于支持对象之间的比较。
- 该方法传入的第二个参数
other
指的是另一个参与比较的对象,而self
参数指的是对象本身。 - 该方法的返回值是 True 或 False。
python
class Student1:
def __init__(self, name, age):
self.name = name
self.age = age
def __lt__(self, other):
return self.age < other.age
stu1 = Student1('Jack', 12)
stu2 = Student1('Jack', 15)
# stu2对应方法中的other参数
print(stu1 < stu2) # True
其他魔术方法:
__le__
:用于支持对象之间进行比较的<=
和>=
两种运算符。- 用法和
__lt__
一模一样。
- 用法和
__eq__
:用于支持对象之间进行比较的==
运算符。- 用法和
__lt__
一模一样。
- 用法和
1.5 私有成员
- 私有成员不能被类对象访问和使用。
- 私有成员能被类中的其他成员访问和使用。
- 私有成员的定义只需要在方法或变量名之前加
__
两个下划线即可。
python
class Student1:
#成员变量也可以有默认值
__address = '北京'
def __init__(self, name, age):
self.name = name
self.age = age
def myAddress(self):
# 访问私有成员
return self.__address
stu1 = Student1('Jack', 12)
print(stu1.__address) # 对象访问私有成员会报错
print(stu1.myAddress()) # 北京
1.6 继承
1.6.1 单继承&多继承
- 继承表示子类从父类继承成员变量和方法(不含私有),然后子类拥有了继承下来的成员,可以直接使用(就当成子类中也定义了相同的成员)。
- 写法:
class 类名(父类名)
- 可以多继承,多个父类名在括号中使用逗号隔开。
- 如果继承下来的多个父类有同名的成员变量或方法,则以括号中从左到右的顺序来排优先级。
单继承:
python
# 定义父类
class Phone:
IMEI = None
simCard = None
def callBy4G(self):
print('父类Phone的成员方法被调用')
# 子类继承父类
class XIAOMI(Phone):
color = 'green'
x1 = XIAOMI()
print(x1.color) # green
# 子类对象可以直接调用继承下来的父类方法
x1.callBy4G() # 父类Phone的成员方法被调用
多继承:
python
class Phone:
IMEI = None
simCard = None
def callBy4G(self):
print('父类Phone的成员方法被调用')
class NFCReader:
producer = 'NVINDA'
def readNFC(self):
print('读取了NFC')
class HUAWEI(Phone, NFCReader):
# 拥有了继承下来的两个父类的所有成员
pass
1.6.2 复写
- 子类可以复写和父类同名的变量或方法,重写后子类再调用就会调用重写后的而不是父类的。
- 重写的方式是直接定义一个和父类中同名的新方法或新变量。
python
class Phone:
IMEI = None
simCard = None
name = "手机大王"
def callBy4G(self):
print('父类Phone的成员方法被调用')
# 子类继承父类
class XIAOMI(Phone):
color = 'green'
def __init__(self, name):
# 从父类继承下来的属性被修改
self.name = name
# 进行了重写
def callBy4G(self):
print('子类XIAOMI的成员方法被调用')
x1 = XIAOMI("小米手机")
x1.callBy4G() # 调用的是重写后的方法而不是父类的
print(x1.name) # 小米手机
1.6.3 调用父类同名属性
- 子类中调用父类属性有2种方式(仅针对属性,调用父类方法见下述):
父类名.属性名
super().属性名
python
class Phone:
IMEI = None
simCard = '父类的sim卡'
def callBy4G(self):
print('父类Phone的成员方法被调用')
class XIAOMI(Phone):
color = 'green'
simCard = '子类重写的sim卡'
# 进行了重写
def callBy4G(self):
# 调用父类成员
print(super().simCard)
x1 = XIAOMI()
x1.callBy4G() # 父类的sim卡
1.6.4 多态和抽象
- 抽象指的是父类来确定有哪些方法和成员,但什么都不做(pass),具体的实现由子类重写决定。
- 多态指的是参数接收父类,但其实传递的是子类对象,也就是父类抽象,子类实际工作。
python
# 抽象类中定义抽象方法
class Animal:
def noise(self):
pass
# 定义子类
class Dog(Animal):
def noise(self):
print("I am a dog")
# 参数接受一个父类对象
def makeNoise(animal: Animal):
animal.noise()
# 实际传递子类对象
makeNoise(Dog()) # I am a dog
1.7 自定义属性
- 对于一些不需要任何参数,然后返回一个值的成员函数,可以使用
@property
装饰器来将它们变成"属性",以后这些方法就可以当作属性来使用,而不用调用函数。 - 定义后函数名就是属性名。
python
class Student:
baseAge = 12
@property
def age(self):
return self.baseAge * 2
stu1 = Student()
# 把age函数当成一个属性来使用了
print(stu1.age) # 24
二、类型注解
2.1 基本用法
- 对代码实际执行没有任何影响,只是帮助第三方IDE工具做类型提示,如自动补全和类型检查。
- 支持的范围:
- 变量、对象的类型注解。
- 函数的形参和返回值的类型注解。
- 写法:
- 变量、对象的注解直接在原本的代码基础上,变量或者对象之后加
: 类型
即可,其他赋值等代码都不动。- 对象的类型注解就是所属的类。
- 方法的返回值注解:
def 函数名() -> 返回值类型
- 变量、对象的注解直接在原本的代码基础上,变量或者对象之后加

2.2 容器类型注解
- 元组类型设置类型详细注解,需要将每一个元素都标记出来。
- 字典类型设置类型详细注解,需要2个类型,第一个是key第二个是value。
2.3 type定义类型注解
- 除了使用
变量: 类型
这种语法做注解外,也可以在注释中进行类型注解。- 语法:
# type: 类型
- 语法:
2.4 Union联合类型
-
对于可以存放各种类型的容器来说,如果一个一个的把所有元素的类型都按顺序写出来会比较麻烦,可以使用联合类型指出所有可能的类型即可。
-
用法:先从typing导包,然后
Union[所有可能的类型逗号隔开]
:
python
from typing import Union
# data可能是int, str种的一种
def process_data(data: Union[int, str]) -> None:
if isinstance(data, int):
print(f"Processing integer: {data}")
elif isinstance(data, str):
print(f"Processing string: {data}")
process_data(42)
process_data("Hello")

isinstance
用来判断对象和类的关系,返回 True 或 False:- 第一个参数写对象,第二个参数写类。
三、闭包
- 闭包指的是函数进行嵌套,内部函数使用外层函数的参数,并且外层函数返回值是内层函数本身。
- 如果一个变量只是用来传递给仅一个函数使用,可以使用闭包增加安全性。
python
def outer(num1: str):
def inner(num2: str):
# 内部函数可直接使用外层函数的参数
print(f'我是{num2},我引用了外层的参数{num1}')
# 外层函数直接返回内部函数本身
return inner
# 此时out1就相当于inner函数,当作函数来使用
out1 = outer('Jay')
out1('Tom') # 我是Tom,我引用了外层的参数Jay
-
如果不使用闭包,直接在inner之外定义num1变量,会存在被导包后被其他模块篡改num1变量的风险,而使用闭包避免这种风险,增加安全性。
-
可以在内部函数中使用 nonlocal 关键字修饰外层函数的参数,从而使得内部函数可以修改这个参数的值。
pythondef outer(num1: int): def inner(num2: int): # 使用nonlocal关键字修饰外层函数的参数,从而可以修改他 nonlocal num1 num1 += num2 return num1 return inner fn = outer(10) print(fn(20)) # 30 print(fn(20)) # 50
- 注意:观察上方代码的最后两行,并不是两次输出都是30,那么得出结论:外层函数的变量在内层函数是共用的,也就是一旦外层函数的参数被修改,那么对内层函数不论调用几次都整体生效,不会重置。
-
闭包优缺点:
- 优点:
- 无需定义全局变量即可通过内层函数持续访问、修改它的参数。
- 增加代码安全性。
- 缺点:
- 内部函数持续引用外部函数的值,会导致这一部分内存空间不释放,占用内存(但是几个变量不释放的内存可以忽略不计)。
- 优点:
四、装饰器
- 装饰器也是闭包,其作用是在不改变原有函数功能的基础上,为函数增加新功能。
- 装饰器本质上是一个接受函数作为参数并返回内层函数的函数。也就是上述闭包模块的外层函数的参数不再是一个变量,而是一个函数。
举例:想要在调用sleep函数前后各执行一些代码:
python
# 外层函数的参数接收一个函数
def outerFunc(func):
def inner():
print('睡觉前做的事情')
func()
print('睡觉后做的事情')
return inner
def sleep():
print('我要睡觉了啊')
inner = outerFunc(sleep)
inner()
"""
睡觉前做的事情
我要睡觉了啊
睡觉后做的事情
"""
代码可使用装饰器的语法糖来写:
python
def outerFunc(func):
def inner():
print('睡觉前做的事情')
func()
print('睡觉后做的事情')
return inner
# 使用@+外层函数名的方式
@outerFunc
def sleep():
print('我要睡觉了啊')
# 直接调用sleep函数即可实现上述功能
sleep()
"""
睡觉前做的事情
我要睡觉了啊
睡觉后做的事情
"""
- 在这个例子中,自定义的
@outerFunc
就是一个装饰器,sleep()
是被装饰器修饰的函数。调用sleep()
其实是在调用外层函数outerFunc()
的内层方法inner()
,且外层方法的参数是被修饰的sleep()
函数。 sleep()
函数被修饰,所以调用sleep()
函数就是修饰后的内容,而不是函数原本的内容。
总结一下具体写法:
- 想要修饰哪个函数就给哪个函数加装饰器。
- 定义一个与装饰器同名的外层函数,并接收一个参数(这个参数实际就是被修饰的函数)。
- 定义内层函数(这是将要被实际执行的函数),并在内层函数中使用外层函数的参数(被修饰的函数)。
- 直接调用被修饰的函数,运行结果就是被修饰后的代码(内层函数)。
注意:外层函数要在被修饰的函数之前定义,因为Python代码从上往下执行,否则Python识别不到当前行之后的内容。
五、构造方法重写补充
注意事项:
- 在Python中,如果子类没有显式定义构造函数(
__init__
方法),当实例化子类对象时,Python 会自动调用父类的构造函数。这是 Python 的默认行为,以使用父类的构造方法提供的功能。 - 如果需要在子类中添加更多的初始化逻辑,可以定义子类自己的
__init__
方法,并且一定要在其中调用父类的__init__
方法,确保父类的初始化逻辑也能够执行。 - 调用父类的属性(两种方式无差别):
父类名.属性名
super().属性名
- 调用父类的方法(无论构造方法还是普通方法都要遵守以下两个规则):
父类名.方法名
:一定需要显式传递self
作为第一个参数。super().方法名
:一定不需要显示传递self
作为第一个参数。因为super()
会自动处理这个参数。在多继承情况下更有用,所以推荐以后都使用这种方式。
5.1 子类单继承没有定义构造方法
python
class Base1:
def __init__(self):
print("父类1构造方法执行了!")
class Child(Base1):
# 没有显示调用父类的构造方法
pass
# 创建 Child 类的实例
child = Child()
输出:
tex
父类1构造方法执行了!
5.2 子类单继承重写构造方法
使用super()调用:
python
class Base1:
def __init__(self):
print("父类1构造方法执行了!")
def normalMethod(self):
print("调用了父类的普通方法")
class Child(Base1):
def __init__(self):
super().__init__()
super().normalMethod()
print("子类构造方法执行了!")
# 创建 Child 类的实例
child = Child()
输出:
tex
父类1构造方法执行了!
调用了父类的普通方法
子类构造方法执行了!
使用父类名调用:
python
class Base1:
def __init__(self):
print("父类1构造方法执行了!")
def normalMethod(self):
print("调用了父类的普通方法")
class Child(Base1):
def __init__(self):
Base1.__init__(self)
Base1.normalMethod(self)
print("子类构造方法执行了!")
# 创建 Child 类的实例
child = Child()
5.3 MRO
- 在Python中,使用
super()
函数来调用父类的构造方法遵循了方法解析顺序(Method Resolution Order,MRO)。MRO是Python确定方法继承顺序的一种方式,它是基于C3线性化算法。 super()
的工作方式:在构造方法中使用super().__init__()
时,Python会查找当前类的MRO来确定下一个类,并调用下一个类的构造方法。- 通过
类名.__mro__
可以得到一个类的MRO顺序。
如何确定一个类的MRO呢?比如这段代码:
python
class Base1:
def __init__(self):
print("父类1构造方法执行了!")
def normalMethod(self):
print("调用了父类1的普通方法")
class Base2:
def __init__(self):
print("父类2构造方法执行了!")
def normalMethod(self):
print("调用了父类2的普通方法")
class Child(Base1, Base2):
def __init__(self):
super().__init__()
print(Child.__mro__)
# 输出:(<class '__main__.Child'>, <class '__main__.Base1'>, <class '__main__.Base2'>, <class 'object'>)
1. 首先确定大方向
- 当前基类一定是排在第一位的,然后考虑当前类的父类,按照父类引用的顺序,从左到右给父类排序。
- 如果一个基类有多个父类,那么这个父类的MRO也会按照和子类相同的算法(C3线性化算法)来计算。
- 所有的Python类最终都继承自
object
类,所以object
是MRO中的最后一个元素。 - 最后,Python将当前类、父类的MRO、以及 object 合并起来,形成当前类的MRO。
目前的MRO顺序:Child、Base1的MRO、Base2的MRO、object
。
2. 根据C3线性化算法,以下是确定两个父类MRO的详细步骤:
-
对每个父类,列出它的MRO(这俩父类都没继承其他类):
Base1
的MRO是[Base1, object]
。Base2
的MRO是[Base2, object]
。
-
从两个父类列表的第一个元素开始,如果这个元素在其他列表中也是第一个元素,或者不在其他列表中,那么它就被添加到最终的MRO中。然后从所有包含这个元素的列表中移除这个元素。(可以简单的认为是在剔除重复已有元素)
-
重复上述步骤,直到所有列表都为空。
故最终可以得到以下顺序:
- 选择
Base1
(因为它在第一个列表中,并且不在其他列表中),最终MRO得到[Child, Base1]
。第一个列表的Object不做处理,因为它不满足条件。 - 然后选择第二个列表的
Base2
(因为它不在其他列表中),最终MRO得到[Child, Base1, Base2]
。 - 最后选择
object
,得到最终的MRO :[Child, Base1, Base2, object]
。
如果父类也有继承的类:
python
class BaseA:
def __init__(self):
print("BaseA init")
class BaseB:
def __init__(self):
print("BaseB init")
class Base1(BaseA):
def __init__(self):
super().__init__()
print("Base1 init")
class Base2(BaseB):
def __init__(self):
super().__init__()
print("Base2 init")
class Child(Base1, Base2):
def __init__(self):
super().__init__()
print("Child init")
print(Child.__mro__)
输出:
tex
(<class '__main__.Child'>, <class '__main__.Base1'>, <class '__main__.BaseA'>, <class '__main__.Base2'>, <class '__main__.BaseB'>, <class 'object'>)
注意:只有使用 super().__init__()
方式时才会使用到MRO的逻辑,如果通过父类名调用的话,和MRO无关。
5.3.1 子类多继承没有定义构造方法
如果子类没有定义构造方法,则子类会根据MRO的顺序,只有第一个父类的构造方法会被自动调用,其他父类的构造方法不会被调用:
python
class Base1:
def __init__(self):
print("父类1构造方法执行了!")
class Base2:
def __init__(self):
print("父类2构造方法执行了!")
class Child(Base1, Base2):
pass
child = Child()
#只会输出:父类1构造方法执行了!
5.3.2 子类多继承重写构造方法
注意:子类中如果通过父类名显示调用了父类的构造方法,则没有调用所有父类的构造方法也不会报错,因为Python支持选择性调用父类的构造方法。
多继承更推荐使用 super()
方式调用父类构造方法,因为只使用一个 super()
就可以自动加载所有的父类构造方法而不用一个个去调用:
python
class Base1:
def __init__(self):
print("父类1构造方法执行了!")
class Base2:
def __init__(self):
print("父类2构造方法执行了!")
class Child(Base2, Base1):
def __init__(self):
super().__init__()
print("Child init")
child = Child()
输出:
tex
父类2构造方法执行了!
Child init
为什么子类使用了 super()
方式,但是只调用了一个父类的构造方法解释:
Child类的MRO顺序:(<class '__main__.Child'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class 'object'>)
super().__init__()
在Child
的构造方法中被调用,它根据MRO调用Base2
的构造方法。- 但是
Base2
中没有使用super()
,所以就不会再继续往上调用了(只有遇到了 super() 方法才会使用MRO逻辑)。
综合示例:
python
class BaseA:
def __init__(self):
super().__init__()
print("BaseA init")
class BaseB:
def __init__(self):
print("BaseB init")
class Base1(BaseA):
def __init__(self):
super().__init__()
print("Base1 init")
class Base2(BaseB):
def __init__(self):
super().__init__()
print("Base2 init")
class Child(Base1, Base2):
def __init__(self):
super().__init__()
print("Child init")
# 创建 Child 类的实例
child = Child()
Child的MRO:
(<class '__main__.Child'>, <class '__main__.Base1'>, <class '__main__.BaseA'>, <class '__main__.Base2'>, <class '__main__.BaseB'>, <class 'object'>)
。
运行结果:
tex
BaseB init
Base2 init
BaseA init
Base1 init
Child init
注意:调用完最上层的构造方法之后要开始回溯。