面向对象编程的核心价值在于用更接近人类思维的方式组织代码:把数据和行为捆绑成对象,再通过对象之间的协作来解决问题。而封装、继承、多态,就是实现这一目标的三根支柱。
封装:隐藏细节,暴露接口
封装的本质是控制访问权限,让对象内部的状态(数据)和实现细节不被外界随意修改,只通过公开的方法与外界交互。
- 为什么要封装?
- 防止数据被意外修改:比如一个
BankAccount对象的余额,如果任何人都能直接account.balance = -500,业务逻辑就崩了。 - 降低耦合:你只要知道调用
deposit(100)会存钱,不需要知道内部是如何记录流水、更新数据库的。将来内部实现改成分布式事务,调用方代码一行不用动。 - 便于维护:把所有对某一状态的修改收敛到固定方法中,要查问题或改逻辑时,改一处就行。
- Python 中的实现方式
- 命名约定:Python 没有真正的
private关键字,靠的是程序员约定。 _name:单下划线前缀,表示“受保护的”,意思是“我不希望你在外部使用,但你真要用也可以”。name:双下划线前缀,会触发名称改写(name mangling),外部无法直接通过obj.name访问,用来避免子类无意覆盖父类属性。- property 装饰器:把方法伪装成属性,让你可以在“读/写属性”时执行代码(比如校验数值范围),同时保持调用方语法不变。
class BankAccount:
def __init__(self, owner):
self.owner = owner
self._balance = 0 # 受保护属性,约定不要外部直接改
@property
def balance(self):
return self._balance # 通过属性方式读取余额
def deposit(self, amount):
if amount <= 0:
raise ValueError("存款金额必须为正数")
self._balance += amount
这里,外界通过 account.balance 查看余额,通过 account.deposit(100) 存钱,无法直接 account._balance = -500 破坏规则(除非刻意无视约定)。
继承:复用代码,建立层次
继承让一个子类可以获取父类的属性和方法,从而实现代码复用和类型层级的构建。
- 什么时候用继承?
- 多个类有明显的“is-a”关系:
Cat是一种Animal,AdminUser是一种User。 - 需要复用父类已有的实现,子类只增加或修改部分行为。
- Python 中的继承语法
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self): # 重写父类方法
return "Woof!"
- 多继承与 MRO
Python 支持多继承——一个子类可以从多个父类继承,这能灵活组合功能,但也可能引发冲突:如果两个父类都有同名方法,该用谁的?Python 使用 C3 线性化算法 计算出一个方法解析顺序(MRO,Method Resolution Order),确定了属性查找的顺序。
你可以通过 ClassName.mro 或 ClassName.mro() 查看这个顺序。核心规则是:子类优先于父类,书写顺序靠左的父类优先,并且所有父类在链条中必须保持正确的先后关系。
- super() 的正确用法
super() 不是简单地“调用父类方法”,而是沿着 MRO 链向后查找下一个类的方法。这是理解多继承行为的关键。
- 单继承中,
super().init()就是调用父类的init,让你在子类初始化时也能执行父类的初始化逻辑。 - 多继承中,
super()会严格按照 MRO 顺序调用,确保每个父类的方法都被执行一次,从而实现“协作式多重继承”。
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
super().__init__()
print("B")
class C(A):
def __init__(self):
super().__init__()
print("C")
class D(B, C):
def __init__(self):
super().__init__()
print("D")
D() # 输出:A -> C -> B -> D (注意不是 A->B->C,因为 MRO 是 D->B->C->A)
实用建议:除非真的需要组合多个父类的功能,否则优先使用单继承或“组合”(把另一个类实例当作属性),这样代码更简单、更不容易出错。
多态:同一个接口,不同的行为
多态是指不同的对象在响应同一个方法时,可以有不同的行为。它是“面向对象”的灵魂,让代码可以围绕抽象接口编写,而不必关心具体类型。
- 最经典的例子:
class Cat:
def speak(self):
return "Meow"
class Dog:
def speak(self):
return "Woof"
def animal_sound(animal):
print(animal.speak())
animal_sound(Cat()) # Meow
animal_sound(Dog()) # Woof
animal_sound 函数不关心传入的是 Cat 还是 Dog,只要求对方有个 speak() 方法。这就是鸭子类型(Duck Typing)—— “如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子”。
- Python 中的多态是天然的
因为 Python 是动态类型的,变量没有固定类型,方法调用在运行时才绑定到具体对象上。你不需要像 Java 那样通过继承一个抽象基类或接口来获得多态性,任何拥有同名方法的对象都可以互相替换。
- 多态如何让代码更好维护?
假设你写了一个文件处理系统,开始只支持本地磁盘文件。后来要加云存储。只要云存储类实现了相同的 read()、write() 方法,业务代码一个字都不用改。这就是多态的价值——面向抽象编程,不面向具体实现。
小结:封装让你控制谁能动数据,继承让你复用好已有的代码结构,多态让你写出可扩展、易替换的灵活系统。三者加在一起,就是面向对象编程最基础也最强大的武器。在 Python 里,这些特性的实现都遵循“简洁胜于复杂”的思路,用起来很自然,但要真正用好,还需要理解 MRO、super() 和鸭子类型的本质。