面向对象编程(OOP)在 Python 中的实现------类、继承与特殊方法
写给习惯 POJO、getter/setter 和 final class 的你
本文是《从 Java 到 Python:一位后端工程师的零基础学习之旅》系列的第五篇。
在 Java 中,OOP 是构建大型系统的基石:
- 一切皆对象
- 类必须显式定义字段、构造函数、访问器
- 继承、封装、多态是三大支柱
而 Python 虽然也支持完整的面向对象编程,但它的态度更务实:
"如果一个功能可以用简单函数实现,就不要强行用类。"
但这并不意味着 Python 的 OOP 能力弱------恰恰相反,它更灵活、更动态,甚至能实现 Java 无法做到的行为(比如运行时修改类结构)。
今天,我们就从类定义开始,一步步对比 Java 与 Python 的 OOP 实现,并重点讲解那些让 Python 对象"活起来"的特殊方法(Magic Methods)。
一、类定义:无需 public,无需字段声明
Java(显式、静态):
java
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
}
Python(简洁、动态):
python
# student.py
class Student:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
调用:
python
s = Student("Alice", 20)
print(s.name) # Alice
s.age = 21 # 直接赋值,无需 setter
🔍 关键差异:
- 无
public/private访问控制(靠命名约定) - 无字段预先声明 (属性在
__init__中动态创建) - 无需 getter/setter(直接访问属性)
💡 Python 哲学:"我们都是 consenting adults."
意思是:开发者知道自己在做什么,不需要语言强行限制访问。
二、封装:用命名约定代替 private
Java 用 private 隐藏内部状态,Python 用命名约定:
|----------|-------|----------------------------------|
| 命名 | 含义 | 说明 |
| name | 公共属性 | 可自由访问 |
| _name | "受保护" | 约定:不应直接访问(类似 Java 的 protected) |
| __name | "私有" | 名称改写(name mangling),避免子类意外覆盖 |
python
class BankAccount:
def __init__(self, balance: float):
self._balance = balance # 约定为内部使用
self.__pin = "1234" # 名称改写为 _BankAccount__pin
def deposit(self, amount: float):
self._balance += amount
def get_pin(self) -> str:
return self.__pin
⚠️ 注意:__pin 并非真正私有!仍可通过 account._BankAccount__pin 访问。
Python 选择信任开发者,而非强制隔离。
三、属性(Property):优雅的 getter/setter
虽然 Python 不需要 getter/setter,但当你需要在赋值时加校验逻辑 ,可以用 @property:
python
class Student:
def __init__(self, name: str, age: int):
self.name = name
self._age = age
@property
def age(self) -> int:
return self._age
@age.setter
def age(self, value: int):
if value < 0:
raise ValueError("Age cannot be negative")
self._age = value
使用:
python
s = Student("Bob", 18)
s.age = 19 # 调用 setter
print(s.age) # 调用 getter → 19
s.age = -5 # 抛出 ValueError
✅ 对外表现:s.age 像普通属性
✅ 对内实现:可加入逻辑校验
💡 类似 Java 的 @Data + 自定义 setter,但更轻量
四、继承:更灵活的多继承
Java(单继承):
python
class Person { /* ... */ }
class Student extends Person { /* ... */ }
Python(支持多继承):
python
class Person:
def __init__(self, name: str):
self.name = name
class Learner:
def study(self):
print("Studying...")
class Student(Person, Learner): # 同时继承两个类
pass
s = Student("Alice")
s.study() # Studying...
⚠️ 多继承需谨慎(方法解析顺序 MRO 可能复杂)
✅ 但配合 Mixin 模式,可实现强大的功能组合(类似 Java 的接口 default 方法)
五、特殊方法(Magic Methods):让对象"像原生类型一样工作"
这是 Python OOP 最强大的部分!
通过实现 __xxx__ 方法,你可以让自定义对象支持 +、len()、str()、in 等操作。
1. __str__ vs __repr__(≈ toString())
python
class Student:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def __str__(self):
return f"Student({self.name}, {self.age})"
def __repr__(self):
return f"Student(name='{self.name}', age={self.age})"
使用:
python
s = Student("Alice", 20)
print(s) # Student(Alice, 20) → 调用 __str__
print([s]) # [Student(name='Alice', age=20)] → 调用 __repr__
✅ __str__:给人看(日志、UI)
✅ __repr__:给开发者看(调试、日志),应尽量可 eval 重建
2. __eq__(定义相等逻辑)
python
def __eq__(self, other):
if not isinstance(other, Student):
return False
return self.name == other.name and self.age == other.age
现在 s1 == s2 会比较内容,而非内存地址(Java 需重写 equals())。
3. __len__, __getitem__(让对象像列表)
python
class GradeBook:
def __init__(self, grades: list[int]):
self.grades = grades
def __len__(self):
return len(self.grades)
def __getitem__(self, index):
return self.grades[index]
使用:
python
book = GradeBook([90, 85, 95])
print(len(book)) # 3
print(book[0]) # 90
for g in book: # 支持 for 循环!
print(g)
🌟 这就是 Python 的"协议"思想:不关心类型,只关心行为(Duck Typing)
六、数据类(dataclasses):告别样板代码
Java 开发者常抱怨 POJO 写起来冗长。
Python 3.7+ 提供 @dataclass,自动生成 __init__、__repr__、__eq__:
python
from dataclasses import dataclass
@dataclass
class Student:
name: str
age: int
active: bool = True # 默认值
等价于手动写:
python
class Student:
def __init__(self, name: str, age: int, active: bool = True):
self.name = name
self.age = age
self.active = active
def __repr__(self): ...
def __eq__(self, other): ...
✅ 支持类型提示
✅ 自动支持 == 比较
✅ 可通过 @dataclass(frozen=True) 实现不可变对象(类似 Java 的 record)
七、对比总结:Java OOP vs Python OOP
|---------------|----------------------------|---------------------------|
| 特性 | Java | Python |
| 类定义 | 必须 public,文件名匹配 | 任意命名,无需访问修饰符 |
| 字段 | 需预先声明 | 动态创建 |
| 封装 | private 强制隐藏 | _ / __ 命名约定 |
| getter/setter | 必须手动写或用 Lombok | 直接访问,或用 @property |
| 继承 | 单继承 + 接口 | 支持多继承(谨慎使用) |
| 相等性 | 重写 equals() | 实现 __eq__ |
| 字符串表示 | 重写 toString() | 实现 __str__ / __repr__ |
| 数据类 | record(Java 14+)或 Lombok | @dataclass(内置) |
| 多态 | 通过接口/抽象类 | 通过 Duck Typing("像鸭子就当鸭子") |
结语:OOP 不是目的,而是工具
Java 把 OOP 作为默认范式 ,而 Python 把 OOP 作为可选工具 。
你可以用类构建复杂系统,也可以用函数组合解决简单问题。
真正重要的是:选择最清晰、最可维护的方式表达你的逻辑。
当你开始用 __str__ 让日志更友好,用 @property 加入校验,用 @dataclass 消除样板代码,
你会感受到 Python OOP 的优雅------它不强迫你,但始终为你准备好更简洁的表达。