1、封装
封装是面向对象编程的三大核心特性之一(另两个是继承和多态)。它指的是将数据(属性)和操作数据的方法(行为)捆绑在一起,形成一个独立的单元——对象。同时,封装也强调对内部实现细节的隐藏,只对外提供有限且明确的接口,以保护数据的完整性和安全性。
封装的核心思想是“高内聚,低耦合”。
● 高内聚: 指一个模块内部的元素(数据和方法)彼此紧密相关,共同完成一个单一的、明确的功能。
● 低耦合: 指模块之间相互依赖的程度低,一个模块的改变对其他模块的影响尽可能小。
在Python中,封装的实现主要依赖于以下几个方面:
-
捆绑数据和方法: 这是通过在类中定义属性和方法自然实现的。一个 Person 类会包含 name, age 等属性,以及 walk(), eat() 等方法,这些都属于 Person 对象。
-
信息隐藏(访问控制): 这是封装的关键部分。Python不像Java或C++那样提供严格的 private 或 protected 关键字来强制限制访问。Python采用的是一种约定俗成的访问控制机制:
-
公共成员 (Public Members): 任何不以一个或两个下划线开头的属性和方法都被认为是公共的,可以从类的外部直接访问。这是Python中默认的访问级别。
-
受保护成员 (Protected Members): 以一个下划线
_
开头的属性和方法(例如_protected_attribute
或_protected_method()
)被约定为“受保护的”。这意味着它们应该被视为类的内部实现细节,不鼓励从外部直接访问,但从技术上讲,它们仍然是可访问的。这是一种给开发者看的信号。 -
私有成员 (Private Members): 以两个下划线
__
开头(但不能以两个下划线结尾,例如__private_attribute
或__private_method()
)的属性和方法被约定为“私有的”。Python解释器会对这类名称进行名称修饰(Name Mangling),使其在类的外部难以直接访问。
-
-
属性访问器 (Getters/Setters) 和 @property 装饰器:
-
虽然Python不强制私有化,但为了更好地控制属性的读取和写入,通常会使用 getter 和 setter 方法。
-
Python提供了
@property
装饰器,可以将一个方法伪装成属性,从而在不改变外部访问方式的情况下,在内部对属性的读写进行逻辑控制和验证。这使得属性的访问方式更加“Pythonic”。
-
● 名称修饰 (Name Mangling) 的原理: 当Python解释器遇到以双下划线
__
开头(且不以双下划线结尾)的属性或方法名时,它会在编译阶段将其名称进行修改,变为_ClassName__attribute_name
的形式。例如,__private_attribute
在 MyClass 中会被转换为_MyClass__private_attribute
。这种机制使得从外部直接通过obj.__private_attribute
访问变得困难,因为外部代码并不知道这个被修饰后的名称。但如果知道修饰后的名称,仍然可以访问(这说明Python的“私有”是约定而非强制)。●
@property
装饰器的原理:@property
是一个内置装饰器,它将一个方法转换为一个属性访问器。当你在类中定义一个@property
方法时,它实际上创建了一个 property 对象。这个 property 对象内部包含了三个可选的方法:○ fget:用于获取属性值(
@property
装饰的方法)。○ fset:用于设置属性值(通过
@<property_name>.setter
装饰的方法)。○ fdel:用于删除属性值(通过
@<property_name>.deleter
装饰的方法)。 当你通过obj.attribute
访问时,实际上是调用了 fget 方法;当你通过obj.attribute = value
赋值时,实际上是调用了 fset 方法。这使得你可以在属性的读写操作中嵌入自定义逻辑,实现更精细的封装。
来看一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/3 17:08
class BankAccount:
def __init__(self, initial_balance):
self.balance = initial_balance
# 受保护成员:以单下划线开头,约定为内部使用
self._account_number = "123456789"
# 私有成员:以双下划线开头,Python会进行名称修饰
self.__balance = initial_balance # 实际存储余额的“私有”属性
# 使用 @property 实现对 __balance 的封装
@property
def balance(self):
"""
getter 方法:允许外部通过属性方式访问余额,但可以在内部添加逻辑。
"""
print("正在获取余额...")
return self.__balance
@balance.setter
def balance(self, value):
"""
setter 方法:允许外部通过属性方式设置余额,但可以在内部进行验证。
"""
if not isinstance(value, (int, float)):
raise TypeError("余额必须是数字!")
if value < 0:
print("警告: 余额不能为负数,已重置为 0。")
self.__balance = 0
else:
print("正在调用setter方法...")
print(f"正在设置余额为{value}")
self.__balance = value
# 公共方法,提供操作数据的接口
def deposit(self, amount):
"""
存款操作
"""
if amount <= 0:
print("存款金额不可小于等于0")
else:
self.balance += amount # 通过 setter 修改余额
print(f"已经成功存款:{amount}元,现余额:{self.balance}元")
def withdraw(self, amount):
"""
取款操作
"""
if amount > 0 and self.balance >= amount:
self.balance -= amount
print(f"已经成功取款{amount}元,现余额:{self.balance}元")
else:
print(f"余额不足,无法取出 {amount} 元。当前余额: {self.balance} 元。")
# 演示受保护方法
def _log_transaction(self, transaction_type, amount):
"""受保护的方法,用于内部记录交易日志。"""
print(f"内部日志: {transaction_type} {amount} 元。")
# 演示私有方法
def __calculate_interest(self):
"""私有方法,计算利息(仅供内部使用)。"""
print("正在秘密计算利息...")
return self.__balance * 0.01
def apply_monthly_interest(self):
"""公共方法,调用私有方法。"""
interest = self.__calculate_interest()
self.balance += interest
print(f"已应用月利息:{interest}元")
account = BankAccount(1000)
# 访问属性 (通过 @property 调用的 getter)
print(f"当前余额:{account.balance}")
# 修改属性 (通过 @property 调用的 setter)
account.balance = 1200
print(f"当前余额:{account.balance}")
# 触发setter中的验证逻辑
account.balance = -200
print(f"当前余额:{account.balance}")
# 调用公用方法
account.deposit(300)
account.withdraw(200)
account.withdraw(500)
account.apply_monthly_interest()
# 尝试访问 受保护成员(技术上可行,但是不推荐)
print(f"账户号码(受保护):{account._account_number}")
# 尝试直接访问私有成员(失败,因为名称已经被修饰)
try:
print(account.__balnace)
except AttributeError as e:
print(f"报错:{e}")
# 尝试访问被名称修饰之后的私有成员(不推荐)
print(account._BankAccount__balance)
2、继承
面向对象编程的另一个核心特性是继承,它允许一个类(子类/派生类)从另一个类(父类/基类)中获取(继承)属性和方法。继承促进代码重用,建立了类之间is a
的关系(比如,狗是一种动物)
继承是实现代码重用和构建类层次结构的关键机制。
- 父类:被继承的类,定义了通用的属性和行为。
- 子类:继承父类的类。子类会自动拥有父类的所有公共和受保护属性及方法。子类可以在继承的基础上添加新的属性和方法,或者重写(覆盖)父类的方法以改变其行为。
is a
的关系:继承表达的是一种is a
的关系,比如狗是一种动物等等。
Python中的继承:
- 单继承:一个子类只继承一个父类,语法就是
class ChildClass(ParentClass):
- 多重继承:一个子类可以继承多个父类,语法是
class ChildClass(ParentClass1, ParentClass2):
- 注意:菱形问题,即当一个类从2个或者更多父类继承,而这些父类又共同继承自同一个祖先类时,方法解析顺序会变得复杂。Python通过MRO(Method Resolution Order)来解决这个问题
super()
函数:用于调用父类(或更广义地说,MRO中的下一个类)的方法。最常见的用法是在子类的__init__
方法中调用父类的__init__
方法,以确保父类的属性得到正确初始化。- 语法:
super().__init__(args)
或super().method_name(args)
。 super()
并不是简单地调用“父类”的方法,它根据当前类的MRO来确定下一个要调用的类。这使得它在多重继承中非常有用,能够确保所有父类的初始化方法都被调用,并且方法调用遵循正确的顺序。
- 语法:
isinstance() 和 issubclass():
● isinstance(obj, Class):检查 obj 是否是 Class 的实例,或者 Class 的子类的实例。
● issubclass(SubClass, SuperClass):检查 SubClass 是否是 SuperClass 的子类(包括自身)。
● 属性和方法的查找: 当你通过一个实例调用方法或访问属性时(例如
dog_instance.bark()
),Python会首先在实例自身的__dict__
中查找。如果找不到,它会沿着该实例所属类的MRO(Method Resolution Order)链向上查找。MRO 定义了类及其所有父类(包括多重继承中的所有基类)的搜索顺序。● MRO (Method Resolution Order) 的原理:
○ MRO 是一个线性化的列表,它决定了Python在查找方法或属性时,应该按照什么顺序遍历一个类的继承层次结构。
○ 对于单继承,MRO很简单,就是
当前类 -> 父类 -> 祖父类 -> ... -> object
。○ 对于多重继承,Python 3 及其以后的版本使用 C3 线性化算法来计算MRO。C3算法保证了以下几点:
- 子类优先于父类。
- 多个父类之间的顺序保留(按照在类定义中出现的顺序)。
- 单调性(如果一个类在MRO中出现在另一个类之前,那么在所有继承自它们的子类的MRO中,这个顺序也会保持)。
○ 你可以通过
ClassName.__mro__
属性或help(ClassName)
来查看一个类的MRO。● super() 的原理: super() 函数返回一个代理对象(proxy object),它能够根据当前类的MRO来查找并调用下一个类的方法。它不是简单地调用父类的方法,而是调用MRO中当前类之后(下一个)的类的方法。这在多重继承中尤为重要,因为它确保了所有共同祖先的方法都能被正确调用,避免了重复调用和遗漏。
(1)单继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 12:37
class Animal:
"""
父类:动物
"""
def __init__(self, name):
self.name = name
print(f"动物:{self.name}实例创建好了")
def speak(self):
"""
动物发声通用的方法
"""
print(f"{self.name}发出声音...")
class Dog(Animal):
def __init__(self, name, breed):
# 调用父类的构造函数,初始化父类的属性
super().__init__(name)
self.breed = breed
print(f"Dog{self.name}({self.breed})实例创建好了")
def bark(self):
"""
狗的特有的行为
"""
print(f"{self.name}bark 很大声...")
# 创建子类的实例
my_dog = Dog("Buddy", "Golden Retriever")
my_dog.speak() # 调用继承自Animal的方法
my_dog.bark() # 调用Dog自己的方法
print(f"my_dog是Animal的实例吗?{isinstance(my_dog, Animal)}")
print(f"Dog是Animal的子类吗?{issubclass(Dog, Animal)}")
(2)多重继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 12:49
class Flyer:
"""
父类:会飞的生物
"""
def fly(self):
print("我会飞...")
class Swimmer:
"""
父类:会游泳的生物
"""
def swim(self):
print("我会游泳...")
class Duck(Flyer, Swimmer):
"""
子类:鸭子,同时继承Flyer和Swimmer
"""
def __init__(self, name):
self.name = name
print(f"鸭子:{self.name}实例创建好了...")
def quack(self):
"""
鸭子叫的方法
"""
print(f"{self.name}嘎嘎叫")
my_duck = Duck("Donald")
my_duck.quack()
my_duck.fly() # 继承自Flyer的方法
my_duck.swim() # 继承自Swimmer的方法
print(f"Duck的MRO:{Duck.__mro__}")
(3)菱形问题(MRO)
下面例子中,确保A的m1方法只被调用一次,避免了菱形问题的中的重复调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 12:55
class A:
def m1(self):
print("类A的m1方法被调用...")
class B(A):
def m1(self):
print("类B的m1方法被调用...")
super().m1() # 调用A的m1方法
class C(A):
def m1(self):
print("类C的m1方法被调用...")
super().m1() # 调用A的m1方法
class D(B, C):
def m1(self):
print("类D的m1方法被调用...")
super().m1() # 调用A的m1方法
d = D()
d.m1()
print(D.__mro__)
3、方法重写
方法重写(Method Overriding)是指子类定义了一个与父类中同名的方法,以提供自己特有的实现。当通过子类实例调用该方法时,会执行子类中的版本,而不是父类中的版本。
方法重写是继承机制的重要组成部分,它允许子类修改或扩展父类的行为,以适应更具体的场景。
● 目的:
○ 特殊化行为: 子类需要对父类提供的通用行为进行定制。
○ 扩展功能: 在保留父类原有功能的基础上,添加额外的逻辑。
○ 实现抽象方法: 如果父类定义了抽象方法(通过 abc 模块),子类必须重写这些方法才能被实例化。
● 调用父类被重写的方法:
○ 在子类重写的方法中,你可能仍然需要调用父类中被重写的方法,以保留其原有功能,并在其基础上进行扩展。
○ 这通常通过 super().method_name(args)
来实现。
○ 也可以直接通过 ParentClassName.method_name(self, args)
来调用,但 super()
更推荐,因为它在多重继承中能正确处理MRO。
● 属性查找机制 (MRO) 的作用: 当你通过一个子类实例调用一个方法时(例如
child_obj.some_method()
),Python会按照该子类的MRO(Method Resolution Order)来查找some_method
。
- 首先,它会在子类自身的
__dict__
中查找some_method
。- 如果子类定义了
some_method
(即重写了它),那么就会找到并执行子类中的这个方法。- 如果子类没有定义,它会沿着MRO向上到父类、祖父类等,直到找到第一个定义了
some_method
的类,然后执行那个方法。● 动态分派 (Dynamic Dispatch): 这就是多态的基础。Python在运行时才决定调用哪个具体的方法实现。它不会在编译时就确定,而是根据实际调用方法的对象的类型来查找并执行对应的方法。这种机制使得代码更加灵活,因为你可以用统一的接口(方法名)来操作不同类型的对象,而每个对象会根据自己的类型执行不同的行为。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 13:54
class Animal:
def __init__(self, name):
self.name = name
print(f"动物{self.name}实例被创建")
def speak(self):
"""
动物发声的通用方法
"""
print(f"{self.name} 发出声音...")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name)
self.breed = breed
print(f"狗{self.name}-{self.breed}实例被创建了...")
# 重写父类的speak方法
def speak(self):
print(f"{self.name}-{self.breed}汪汪叫...")
class Cat(Animal):
def __init__(self,name):
super().__init__(name)
# 重写父类的speak方法,并且调用父类的方法
def speak(self):
super().speak()
print(f"{self.name}喵喵叫")
class Bird(Animal):
"""
不重写speak方法
"""
def __init__(self, name):
super().__init__(name)
animal = Animal("通用动物")
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers")
bird = Bird("Tweety")
animal.speak()
dog.speak()
cat.speak()
bird.speak()
animals = [Animal("Zoo 动物"), Dog("Max", "German Shepherd"), Cat("Mittens"), Bird("Parrot")]
for a in animals:
a.speak()
4、object类
object 类是Python中所有类的最终基类。这意味着所有在Python中定义的类(无论是显式继承还是隐式继承)都直接或间接地继承自 object。它提供了所有Python对象共有的基本行为和特殊方法。
在Python 3中,所有的类都是“新式类”(new-style classes),它们都隐式地继承自 object。即使你定义一个简单的类 class MyClass: pass,它也等同于 class MyClass(object): pass
。
object 类定义了许多特殊的“魔术方法”(或称“双下划线方法”、“dunder methods”),这些方法为Python的内置操作提供了默认实现。当你定义一个类并重写这些方法时,你实际上是在定制这些内置操作的行为。
object 类提供的一些重要特殊方法:
● __init__(self, ...)
:对象的构造器(构造函数)。
● __new__(cls, ...)
:对象的创建器(在 __init__
之前调用)。
● __str__(self)
:返回对象的“非正式”字符串表示,供 str() 函数和 print() 函数使用。通常用于用户友好的输出。
● __repr__(self)
:返回对象的“正式”字符串表示,供 repr() 函数和调试器使用。通常应返回一个字符串,该字符串在可能的情况下,可以用来重新创建对象。
● __eq__(self, other)
:定义相等性比较(==
运算符)。
● __ne__(self, other)
:定义不相等性比较(!=
运算符)。
● __lt__(self, other)
:定义小于比较(<
运算符)。
● __le__(self, other)
:定义小于等于比较(<=
运算符)。
● __gt__(self, other)
:定义大于比较(>
运算符)。
● __ge__(self, other)
:定义大于等于比较(>=
运算符)。
● __hash__(self)
:定义对象的哈希值,用于在哈希表(如字典的键、集合的元素)中存储对象。如果一个类重写了 __eq__
,通常也应该重写 __hash__
。可变对象默认不可哈希。
● __delattr__(self, name)
:定义删除属性时的行为(del obj.attr
)。
● __getattr__(self, name)
:定义当访问不存在的属性时,如何处理。
● __setattr__(self, name, value)
:定义设置属性时的行为(obj.attr = value
)。
● __dir__(self)
:定义 dir() 函数的行为。
● __doc__
:类的文档字符串。
● __class__
:指向对象所属的类。
● __dict__
:存储实例属性的字典。
● 统一对象模型: object 类作为所有类的根,确保了Python中所有对象都具有一套共同的基本行为。这使得Python能够实现统一的内存管理、垃圾回收、属性查找机制等。无论你处理的是整数、字符串、列表还是自定义类的实例,它们本质上都是 object 的子类,因此可以以统一的方式进行操作。
● MRO 的终点: 任何类的MRO链最终都会以 object 结束。当Python查找一个方法或属性时,如果一直向上追溯到 object 仍然没有找到,才会抛出 AttributeError。
● 默认行为的提供: object 类提供了许多特殊方法的默认实现。例如,
__str__
的默认实现通常会返回一个包含类名和内存地址的字符串。当你重写这些方法时,你是在定制这些默认行为。● 新式类与旧式类 (Python 2 历史背景): 在Python 2 中,存在“旧式类”(不继承自 object)和“新式类”(继承自 object)。旧式类在多重继承和方法解析方面存在一些问题。Python 3 统一了所有类都是新式类,从而简化了继承模型,并强制使用C3 MRO算法。这个统一是Python设计哲学中“一切皆对象”原则的体现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 14:29
# 显式继承object
class MyCustomObject(object):
"""
一个自定义对象,演示继承自object的默认行为和重写
"""
def __init__(self, value):
self.value = value
# 重写__str__方法,提供用户友好的字符串表示
def __str__(self):
return f"MyCustomObject value是:{self.value}"
# 重写__repr__方法,提供一个官方的、可用于重建的字符串表示
def __repr__(self):
# !r表示调用repr()转换value
return f"MyCustomObject value是:{self.value!r}"
def __eq__(self, other):
if isinstance(other, MyCustomObject):
return self.value == other.value
return NotImplemented # 表示不处理与其他类型的比较
# 如果重写了__eq__且对象可哈希,则应该重写__hash__
def __hash__(self):
return hash(self.value) # 使用value的哈希值作为对象的哈希值
def show_internal_info(self):
print(f"实例属性字典:{self.__dict__}")
print(f"所属的类:{self.__class__}")
print(f"类的基类:{self.__class__.__bases__}")
print(f"类的MRO:{self.__class__.__mro__}")
o1 = MyCustomObject(10)
print(f"print(o1):{o1}") # 调用__str__
print(f"repr(o1):{repr(o1)}") # 调用__repr__
# 默认情况下,如果只定义了__repr__,没有定义__str__, 那么print会调用__repr__
# 调用__eq__
o2 = MyCustomObject(20)
o3 = MyCustomObject(10)
print(o1 == o2)
print(o1 == o3)
# 字典中,__hash__
set1 = {o1, o2, o3}
print(len(set1))
dict1 = {o1: "v1", o2: "v2"}
print(dict1[o3])
o1.show_internal_info()
print(issubclass(MyCustomObject, object))
5、多态
多态是面向对象编程的第三个核心特性。它允许不同类的对象对同一消息(方法调用)作出不同的响应。在Python中,多态主要通过“鸭子类型”(Duck Typing)和抽象基类(Abstract Base Classes, ABCs)来实现。多态的核心在于“一个接口,多种实现”。它使得代码更加灵活、可扩展和易于维护。
(1)鸭子类型
○ Python中多态的主要实现方式。
○ 思想: “如果它走起来像鸭子,叫起来像鸭子,那么它就是一只鸭子。”这意味着,只要一个对象拥有所需的方法(或属性),就可以被当作特定类型来处理,而无需显式声明它继承自某个共同的基类或实现了某个接口。
○ 优点: 极大地提高了代码的灵活性和解耦性。你不需要关心对象的具体类型,只需要关心它是否提供了你所需的方法。
○ 缺点: 缺乏编译时类型检查,错误可能在运行时才暴露。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 15:26
class Duck:
def quack(self):
print("Quack!!!")
def fly(self):
print("鸭子在飞...")
class Plane:
def quack(self): # 尽管是飞机,但也有quack方法
print("飞机也可以quack(像鸭子?)...")
def fly(self):
print("飞机在飞...")
class Person:
def quack(self):
print("人模仿鸭子quack...")
def fly(self):
print("人尝试飞,但是掉下来了...")
def make_it_quack_and_fly(entity):
"""
一个可以接受任何拥有quack和fly的对象的函数
"""
print("一个可以接受任何拥有quack和fly的对象的函数make_it_quack_and_fly被调用")
entity.quack()
entity.fly()
duck = Duck()
plane = Plane()
person = Person()
make_it_quack_and_fly(duck)
make_it_quack_and_fly(plane)
make_it_quack_and_fly(person)
(2)抽象基类 (Abstract Base Classes, ABCs)
○ Python提供了 abc 模块来支持抽象基类。ABCs允许你定义一个接口(即一组必须由子类实现的方法),但不能直接实例化。
○ 用途:
■ 强制实现: 确保子类实现了特定的方法,否则无法实例化。
■ 形式化接口: 为鸭子类型提供一个更明确的契约,提高代码可读性和可维护性。
■ 类型检查: 可以使用 isinstance()
和 issubclass()
对实现了ABC的类进行类型检查。
○ 定义:
■ 继承 abc.ABC
。
■ 使用 @abc.abstractmethod
装饰器标记抽象方法。
● 动态分派 (Dynamic Dispatch): 这是多态的底层机制。当调用一个对象的方法时,Python解释器在运行时根据对象的实际类型来查找并执行对应的方法。它不是在编译时就确定调用哪个方法,而是等到运行时才进行“分派”。
○ 例如,
animal.speak()
:如果 animal 是 Dog 实例,就调用 Dog 类的 speak 方法;如果是 Cat 实例,就调用 Cat 类的 speak 方法。这种运行时查找和执行的机制就是动态分派。● Python的类型系统: Python是一种动态类型语言,变量没有固定的类型,而是引用对象的类型。这种灵活性是鸭子类型的基础。解释器在执行方法调用时,只关心对象是否具有该方法,而不关心对象的声明类型。
● ABCs 的原理:
○ 当一个类继承自
abc.ABC
并包含@abstractmethod
装饰的方法时,该类就不能被直接实例化。○ 当一个子类继承这个ABC时,如果它没有实现所有抽象方法,Python会在尝试实例化该子类时抛出 TypeError。
○ ABCs通过在类创建时修改类的元类(metaclass,通常是
abc.ABCMeta
)来实现这种行为。元类控制着类的创建过程,从而能够强制执行抽象方法的实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 15:33
import abc
class Shape(abc.ABC): # 继承自abc.ABC,使其成为抽象类
"""
抽象基类:图形
定义了所有图形应该有的通用的接口
"""
def __init__(self, name):
self.name = name
@abc.abstractmethod # 抽象方法,子类必须实现
def area(self):
"""
计算图形面积的抽象方法
"""
pass # 抽象方法没有实现体
@abc.abstractmethod
def perimeter(self):
"""
计算图形周长的抽象方法
"""
pass
def describe(self): # 普通方法,子类可以继承,也可以重写
print(f"这是一个{self.name}图形...")
# generic_shape = Shape("Generic") # TypeError: Can't instantiate abstract class Shape with abstract methods area, perimeter
class Rectangle(Shape):
"""
具体子类:矩形,实现Shape的抽象方法
"""
def __init__(self, width, height):
super().__init__("Rectangle")
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.height + self.width)
class Circle(Shape):
"""
具体子类:圆形,实现Shape的抽象方法
"""
PI = 3.14159
def __init__(self, radius):
super().__init__("Circle")
self.radius = radius
def area(self):
return Circle.PI * self.radius * self.radius
def perimeter(self):
return 2 * Circle.PI * self.radius
# 测试
r1 = Rectangle(3, 5)
c1 = Circle(2)
# 使用多态的方式处理不同的形状
shapes = [r1, c1]
for item in shapes:
item.describe()
print(f"面积:{item.area()}")
print(f"周长:{item.perimeter()}")
print(f"是Shape的实例吗?{isinstance(item, Shape)}")
class IncompleteShape(Shape):
"""
未实现抽象方法的子类
"""
def __init__(self):
super().__init__("Incomplete")
try:
o1 = IncompleteShape()
except TypeError as e:
print(f"报错:{e}")
6、特殊方法和特殊属性
特殊方法(Special Methods),也常被称为“魔术方法”(Magic Methods)或“双下划线方法”(Dunder Methods),是Python中以双下划线开头和结尾的方法(例如 __init__, __str__
)。它们允许你定义类的行为,以便与Python的内置函数、运算符和语言特性(如上下文管理器、迭代器)进行交互。特殊属性是类或实例内置的、以双下划线开头和结尾的属性。
特殊方法是Python实现其“数据模型”(Data Model)的核心。通过重写这些特殊方法,你可以让自定义类的对象行为得像内置类型一样,例如支持加法、索引、迭代、上下文管理等。这被称为运算符重载 (Operator Overloading)。
常见且重要的特殊方法:
- 构造与初始化:
○ __new__(cls, ...)
:对象的创建方法,在 __init__
之前调用,负责创建并返回一个实例对象。
○ __init__(self, ...)
:对象的初始化方法,用于设置新创建实例的属性。
○ __del__(self)
:析构方法,当对象被垃圾回收时调用(不保证何时调用)。
- 字符串表示:
○ __str__(self)
:返回对象的“非正式”字符串表示,供 print() 和 str() 使用。
○ __repr__(self)
:返回对象的“正式”字符串表示,供 repr() 和调试器使用,通常应能用于重建对象。
- 比较操作:
○ __eq__(self, other)
:==
运算符。
○ __ne__(self, other)
:!=
运算符。
○ __lt__, __le__, __gt__, __ge
__:<
, <=
, >
, >=
运算符。
- 数值运算:
○ __add__(self, other)
:+
运算符。
○ __sub__(self, other)
:-
运算符。
○ __mul__(self, other)
:*
运算符。
○ __truediv__(self, other)
:/
运算符。
○ __floordiv__(self, other)
://
运算符。
○ __mod__(self, other)
:%
运算符。
○ __pow__(self, other)
:**
运算符。
○ 还有反射版本(如 __radd__
)、原地操作(如 __iadd__
)等。
- 容器类型操作:
○ __len__(self)
:len()
函数。
○ __getitem__(self, key)
:索引访问(obj[key])
和切片访问。
○ __setitem__(self, key, value
):索引赋值(obj[key] = value
)。
○ __delitem__(self, key)
:删除元素(del obj[key]
)。
○ __contains__(self, item)
:in
运算符。
- 可调用对象:
○ __call__(self, *args, **kwargs)
:使实例对象可以像函数一样被调用(obj(...)
)。
- 迭代器:
○ __iter__(self)
:返回一个迭代器对象,用于 for ... in ...
循环。
○ __next__(self)
:迭代器协议的一部分,返回下一个元素。
- 上下文管理器:
○ __enter__(self)
:进入 with 语句块时调用。
○ __exit__(self, exc_type, exc_val, exc_tb)
:退出 with 语句块时调用,无论是否发生异常。
特殊属性:
● __dict__
:存储实例或类的命名空间的字典。
● __class__
:指向对象所属的类。
● __name__
:模块或类的名称。
● __module__
:对象所属的模块名称。
● __doc__
:对象的文档字符串。
● __bases__
:类的直接基类元组。
● __mro__
:类的MRO元组。
● Python数据模型: Python的解释器在执行内置操作(如
+
运算符、len()
函数、for
循环等)时,会查找对象类型中对应的特殊方法。例如,当执行a + b
时,Python会尝试调用a.__add__(b)
。如果 a 没有定义__add__
,它会尝试调用b.__radd__(a)(反射加法)
。● 协议 (Protocols): 特殊方法实现了Python的各种“协议”。例如,
__iter__
和__next__
实现了迭代器协议,__enter__
和__exit__
实现了上下文管理器协议。当一个对象实现了某个协议所需的所有特殊方法时,它就可以被当作符合该协议的类型来使用。● 动态绑定: 与普通方法一样,特殊方法的调用也是动态绑定的。解释器在运行时根据对象的实际类型来查找并调用正确的特殊方法。
● 属性查找: 特殊属性通常是解释器在创建类或实例时自动设置的元数据,或者通过特定的机制(如
__dict__
)提供对内部状态的访问。
(1)字符串表示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 17:28
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
"""
用户友好的字符串表示
"""
return f"(x={self.x}, y={self.y})"
def __repr__(self):
"""
官方的、可以用于重建的字符串表示(重新创建对象)
"""
return f"Point(x={self.x}, y={self.y})"
p = Point(1, 2)
print(p)
p_repr = repr(p)
p_ = eval(p_repr)
print(p_)
(2)比较操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 19:35
class MyNumber:
def __init__(self, value):
self.value = value
def __eq__(self, other):
if isinstance(other, MyNumber):
return self.value == other.value
return NotImplemented # 允许与其他类型比较的时候,如果无法处理就返回NotImplemented
def __lt__(self, other):
if isinstance(other, MyNumber):
return self.value < other.value
return NotImplemented
def __str__(self):
print(f"MyNumber({self.value})")
n1 = MyNumber(10)
n2 = MyNumber(20)
n3 = MyNumber(10)
print(f"n1 == n3:{n1 == n3}")
print(f"n1 < n2?{n1 < n2}")
print(f"n1 == 10?{n1 == 10}")
(3)数值运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 19:45
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented
def __mul__(self, scalar):
"""
标量和向量的乘法
"""
if isinstance(scalar, (int, float)):
return Vector(scalar * self.x, scalar * self.y)
return NotImplemented
def __str__(self):
return f"Vector(x={self.x}, y={self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v_sum = v1 + v2
print(f"{v1} + {v2}= {v_sum}")
v_times_5 = v1 * 5
print(f"{v1}*5={v_times_5}")
(4)容器类型操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 19:50
class MyList:
def __init__(self, *args):
self._data = list(args)
def __len__(self):
return len(self._data)
def __getitem__(self, index):
return self._data[index]
def __setitem__(self, index, value):
self._data[index] = value
def __delitem__(self, index):
del self._data[index]
def __contains__(self, item):
return item in self._data
def __str__(self):
return f"MyList({self._data})"
ml = MyList(10, 20, 20, 30, 40)
print(ml)
print(f"长度:{len(ml)}")
print(ml[2])
print(ml[1:4])
ml[0] = 100
print(ml)
del ml[1]
print(ml)
print(99 in ml)
(5)可调用对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 23:45
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, value):
"""
使得实例可像函数一样被调用
"""
return self.factor * value
double = Multiplier(2)
triple = Multiplier(3)
print(double(5))
print(double.__call__(5)) # 与double(5)等价
print(triple(10))
print(triple.__call__(10)) # 与triple(10)等价
(6)上下文管理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 23:49
class MyFileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
"""
进入with语句块时会调用
"""
print(f"打开文件:{self.filename}")
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
"""
退出with语句块时调用,无论是否发生异常
"""
if self.file:
self.file.close()
print(f"关闭文件:{self.filename}")
if exc_type:
print(f"发生异常:{exc_type.__name__}:{exc_val}")
return False # 返回False表示不处理异常,让其继续传播
with MyFileManager("test_file.txt", "w") as f:
f.write("Hello from context manager...\n")
f.write("this is line 2...")
print("文件写入完成")
try:
with MyFileManager("test_file_error.txt", "w") as f:
f.write("This will cause an error...\n")
f.write("this is line 2...")
raise ValueError("出问题了...")
except ValueError as e:
print(f"报错:{e}")
(7)特殊属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8 23:57
class ExampleClass:
def __init__(self, name):
self.name = name
def my_method(self):
pass
obj = ExampleClass("TestObj")
print(f"对象的实例属性:{obj.__dict__}")
print(f"对象所属的类:{obj.__class__}")
print(f"类的名称:{ExampleClass.__name__}")
print(f"类所在的模块:{ExampleClass.__module__}")
print(f"类的文档字符串:{ExampleClass.__doc__}")
print(f"类的直接基类:{ExampleClass.__bases__}")
print(f"类的MRO:{ExampleClass.__mro__}")