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

面向对象编程(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 的优雅------它不强迫你,但始终为你准备好更简洁的表达。

相关推荐
小龙报14 小时前
【优选算法】双指针专项:1.移动零 2. 复写零 3.快乐数
java·c语言·数据结构·c++·python·算法·面试
IT策士14 小时前
Django 从 0 到 1 打造完整电商平台:我的订单列表与订单详情
后端·python·django
AI行业学习14 小时前
CC-Switch Windows + macOS 下载安装配置全流程
java·开发语言·人工智能·python
Lumbrologist14 小时前
【C++】零基础入门 · 第 3 节:条件判断(if、switch)
开发语言·c++·算法
布吉岛的石头14 小时前
Java 程序员第 22 阶段:Function Call 工具调用实战,Java 封装大模型外部能力
java·人工智能·python
l1t14 小时前
DeepSeek总结的使用实体-组件-系统和基于存在性处理进行Python编程简介
开发语言·python
曲幽14 小时前
FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示?
python·vue3·api·fastapi·web·ant design·view·menu·frontend
Lhan.zzZ15 小时前
使用 ctx.lineDash 根治 QML Canvas 虚线残留问题(支持 Qt 5.12/5.14 等版本)
开发语言·qt
雨落在了我的手上15 小时前
初识java(十一):继承
java·开发语言