面向对象编程(OOP)在 Python 中的实现——类、继承与特殊方法

写给习惯 POJO、getter/setter 和 final class 的你
本文是《从 Java 到 Python:一位后端工程师的零基础学习之旅》系列的第五篇。

在 Java 中,OOP 是构建大型系统的基石:

  • 一切皆对象
  • 类必须显式定义字段、构造函数、访问器
  • 继承、封装、多态是三大支柱

而 Python 虽然也支持完整的面向对象编程,但它的态度更务实:

“如果一个功能可以用简单函数实现,就不要强行用类。”

但这并不意味着 Python 的 OOP 能力弱——恰恰相反,它更灵活、更动态,甚至能实现 Java 无法做到的行为(比如运行时修改类结构)。

今天,我们就从类定义开始,一步步对比 Java 与 Python 的 OOP 实现,并重点讲解那些让 Python 对象“活起来”的特殊方法(Magic Methods)


一、类定义:无需 public,无需字段声明

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(简洁、动态):

# student.py
class Student:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

调用:

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),避免子类意外覆盖

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

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

使用:

s = Student("Bob", 18)
s.age = 19      # 调用 setter
print(s.age)    # 调用 getter → 19
s.age = -5      # 抛出 ValueError

✅ 对外表现:s.age 像普通属性
✅ 对内实现:可加入逻辑校验
💡 类似 Java 的 @Data + 自定义 setter,但更轻量


四、继承:更灵活的多继承

Java(单继承):

class Person { /* ... */ }
class Student extends Person { /* ... */ }

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()

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})"

使用:

s = Student("Alice", 20)
print(s)           # Student(Alice, 20) → 调用 __str__
print([s])         # [Student(name='Alice', age=20)] → 调用 __repr__

__str__:给人看(日志、UI)
__repr__:给开发者看(调试、日志),应尽量可 eval 重建


2. __eq__(定义相等逻辑)

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__(让对象像列表)

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]

使用:

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__

from dataclasses import dataclass

@dataclass
class Student:
    name: str
    age: int
    active: bool = True  # 默认值

等价于手动写:

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 的优雅——它不强迫你,但始终为你准备好更简洁的表达。

更多推荐