Python编程 II 面向对象

Python 是一门面向对象的语言,但是我们仍需要讨论的是 什么是面向对象

什么是对象?

在许多编程书籍和技术博客中,对象常被定义为“一个真实存在的实体”或“方法和属性的集合”。

然而,这些定义往往只停留在表层,并未真正解释“什么是对象”这一核心问题。比如,“实体”本身是什么意思?为什么属性和方法的组合可以称为一个‘对象’?带着这些问题,我们可以从设计层面重新思考对象的本质。

设计层面:对象是面向目的的实体特征与行为的抽象

什么是实体?

动物是不是一种实体?房屋是不是一种实体?房屋?比特币呢?笛卡尔的“我思故我在”呢?

实体并不一定是以“真实存在”作为必要条件的,实体的重要特征是“一种可描述的概念”。比如“独角兽”是一只长有犄角和翅膀的马,比如“西方的恶龙覆盖有厚厚的鳞片能够喷吐火焰”。这些概念的重点不是“真实存在”,而是“可描述”。推而广之,一些抽象概念也可以作为“实体”,比如 FIFO的队列,代理模式中的代理概念。

这样我们就可以得到实体的定义 “一种可描述的概念”(无论它是否真的存在)

那么什么是对象?

我们永远没办法将一个实体完整地用计算机语言去描述。但可以将特征和行为抽象出来,作为我们用计算机去模拟这个物体的方法。这暗含着对象设计是面向功能的,有取舍的。因为我们实际关注的不是实体到底是怎么样的,而是关注实体通过哪些特征和行为达成了我们想要的结果。当我们考虑一头牛的时候,在虚拟世界里,我们不会去考虑牛住在哪里,何时休息。考虑的是我们为了达成目的,需要”供应“什么,“产出”什么。所以我们在设计时真正构建的对象是以目的为核心,对实体特征和行为的抽象。目的是我们的最终结果,而实体的特征和行为是为了达成目的所必须的组件

实现层面:对象是经过结构化组织的内存

内存与对象

程序员对内存和程序结构拥有近乎完全的控制权。他们可以自由地决定每一个内存地址的用途,这在空间受限的场景中显得尤为高效,可以最大限度地压榨出每一字节的潜力。但也导致了一些问题:

  1. 在功能设计的初期就需要周全的考虑对象(结构体)的设计,不然会加大后期的维护,重构,新增功能的复杂度。
  2. 编程人员对程序有绝对的权限,意味着相同的功能可能会设计不同的系统来处理相同的流程,如内存分配/回收。消息处理。对于多人开发的大型系统会增加系统的复杂度。

在这一层面上,一些更现代的编程语言选择放弃了C语言的“自由“,现代系统的设计都是层次化的,当我进行上层数据的处理时,仍然要去关心最底层的内存是如何排布的显然是一个不够高效的做法。因此它们开始去预定义一些结构化的内存,从”内存交给你,你来从0开始完成系统“,到“由我提供一些基础组件,你在这个基础之上组织系统”。通过对内存块的结构化设计,将“设计-实现”的问题,变成了“组织-实现”。

另一方面因为所有基础模块是编程语言预先定义的,所以在后期使用时,作为内存-系统的中间层(如一些虚拟机程序),可以对系统中的内存使用进行更好的控制,这样既可以通过系统的管理,将已加载的功能模块灵活复用,也可以更好的提供系统级的监控管理,像垃圾回收,对象的动态加载创建等,对于更上层的编程人员来说,进行类设计,对象设计。反而是一些顺便带来的好处。

那么什么是面向对象?

当我们明白了对象是一种可描述的概念,并且对象设计的核心是面向功能的,以及为什么使用对象而不是直接使用内存来设计我们的系统。那么面向对象就是在设计系统时,首先完成对系统功能的抽象,将系统的实现变为多个核心对象之间的消息通信和方法调用。如:

  • 对数据进行存储和维护而设计的类,如用户类,仓库类
  • 对数据组织结构进行抽象的类,如数据包类,工厂类
  • 以某种操作流程为核心建模的设计的类,队列、栈

通过这种方式,我们不仅构建了更具结构化的系统,同时也提升了系统的可读性、可维护性与可扩展性 —— 这正是面向对象设计的核心价值所在。

谈谈Python

当我们完全的理解了什么是面向对象之后,这时我们又要再回过头来,谈谈为什么Python是面向对象的。

Python的对象

在我看来,Python的对象设计可以用变态来形容。在它的程序中的任何变量/对象/实例都是被作为结构化的内存存储在系统当中的,并且可以便捷的访问。它提供了一种怪异的自由度,像一家杂货店,仿佛在告诉你,只要你想找任何商品(对象)。都可以便捷的帮你找到,只提供对象的交付,生命周期管理,剩下的任何内容都交由你处理。

作为“组织者”的我们

于是,作为使用 Python 的开发者,我们不再是单纯的“编写者”,而更像是系统的组织者与行为的策划者

我们面对的不是一块空白的内存,而是一张庞杂却灵活的工具清单——类、函数、装饰器、生成器、魔术方法、元类……Python 把构建系统所需的砖瓦都提前摆好,而我们需要做的,是以对象为基石,以功能为目标,组织它们构建出一个可运行、可维护、可理解的系统。

另外虽然面向对象提供了结构化的设计方式,但在某些场景下,简单的函数或模块化设计可能更高效,Python的灵活性允许开发者自由选择。

封装,继承和多态

封装

封装指的是 ,当对象已经被设计为一个完整的整体时,内部的一些属性或方法不再能作为公共功能提供给外界调用。在Python中表现为对方法,类的伪私有化,属性的封装和内部函数的闭包设计

  • 方法和类的伪私有化

当将方法设置为__function_name后,执行过程中会将方法名替换为_ClassName__name,避免方法被直接调用。

1
2
3
4
5
6
7
class MyClass:
def __init__(self):
self.public_var = "我是公开的"
self.__private_var = "我是'私有'的"

def __private_method(self):
return "这是'私有'方法"
  • 属性的封装

将属性设置为私有属性,通过方法调用来完成属性的设置。避免了属性直接通过”.”获取到被篡改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student:
def __init__(self, name, score):
self._name = name # 使用单下划线表示不应直接访问
self._score = score # 使用单下划线表示不应直接访问

@property # 读取器
def name(self):
return self._name

@property # 读取器
def score(self):
return self._score

@score.setter # 设置器
def score(self, value):
if not isinstance(value, int):
raise ValueError("成绩必须是整数")
if value < 0 or value > 100:
raise ValueError("成绩必须在0到100之间")
self._score = value
  • 闭包

闭包提供了一个隐藏函数内部实现,将数据在内部处理的方法,只在外部定义返回的变量,而将具体的过程放在内部进行处理,让外部无非直接访问内部的执行逻辑。python中的闭包不是典型的 OOP 封装,但它提供了类似‘数据私有性’的能力,因此可以理解为一种过程式封装形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def counter_factory(start=0, step=1):
# 在外部函数中定义的变量
count = start

# 内部函数,形成闭包
def counter():
# 使用nonlocal关键字,表明我们要修改外部函数的变量
nonlocal count
# 保存当前值
current = count
# 更新计数
count += step
# 返回计数值
return current

# 返回内部函数,不执行它
return counter

继承

继承指的是,当一个子类对父类进行继承之后,子类拥有了对父类的所有方法和属性的访问权限。(对经过私有声明的属性和方法可以以特殊形式访问到)。这就完成了is-a 的关系,继承通常用于在同一类别下子类的快速创建,以及为多态的实现提供可能。

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
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def make_sound(self):
"""发出声音"""
return "一些声音"
def info(self):
"""显示动物信息"""
return f"名称:{self.name}, 年龄:{self.age}岁"
class Dog(Animal):
"""狗类,继承自动物类"""
def __init__(self, name, age, breed):
# 调用父类的__init__方法
super().__init__(name, age)
self.breed = breed # 狗的品种
def make_sound(self):
"""重写父类的方法"""
return "汪汪汪!"
def info(self):
"""扩展父类的方法"""
basic_info = super().info()
# 添加子类特有的信息
return f"{basic_info}, 品种:{self.breed}"
def wag_tail(self):
"""子类特有的方法"""
return f"{self.name}摇了摇尾巴"
# 创建实例并测试
def main():
# 创建一个Animal实例
generic_animal = Animal("小动物", 3)
print(generic_animal.info())
print(generic_animal.make_sound())
print("-" * 30)
# 创建一个Dog实例
my_dog = Dog("旺财", 5, "金毛")
# 测试继承的属性和方法
print(my_dog.info()) # 使用重写的方法
print(my_dog.make_sound()) # 使用重写的方法
print(my_dog.wag_tail()) # 使用子类特有的方法

if __name__ == "__main__":
main()

多态

多态指的是,当一个函数接收父类型作为参数时,通过传入继承自父类的子类。调用同一个方法会得到不同的行为,在python中,因为弱类型的实现,不太好关注到多态的明显特征。因为在函数调用时,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
class Animal:
def speak(self):
pass # 基类中的方法是抽象的

# 定义子类,继承自Animal
class Dog(Animal):
def speak(self):
return "汪汪!"

class Cat(Animal):
def speak(self):
return "喵喵!"

class Duck(Animal):
def speak(self):
return "嘎嘎!"

# 多态函数
def animal_sound(animal):
# 同一个函数调用会根据对象的实际类型产生不同的结果
# 这就是多态的体现
return animal.speak()

# 创建不同动物的实例
dog = Dog()
cat = Cat()
duck = Duck()

# 使用相同的函数调用不同类型的对象
print(animal_sound(dog)) # 输出: 汪汪!
print(animal_sound(cat)) # 输出: 喵喵!
print(animal_sound(duck)) # 输出: 嘎嘎!

Python编程 II 面向对象
http://gadoid.io/2025/04/15/Python编程-II-面向对象/
作者
Codfish
发布于
2025年4月15日
许可协议