# 第 3-1 课:Python 与 Java 的类和对象 ## 一、本课定位 你已经有成熟的 Java 开发经验,因此本阶段不再从“什么是对象”开始长篇讲解。本课采用差异驱动方式:先建立 Java 写法与 Python 写法的对应关系,再通过代码和练习形成 Python 语感。 即使没有完整阅读本文,也请至少运行示例并完成 `practice.py`。示例输出和练习题中会重复本课的关键差异。 ## 二、本课目标 完成本课后,你将能够: 1. 使用 Python 定义类、初始化对象并调用实例方法; 2. 理解 `self` 与 Java `this` 的关系和语法差异; 3. 区分实例属性与类属性; 4. 区分实例方法、类方法和静态方法; 5. 避免把 Java 的字段声明、构造器和 `new` 原样搬到 Python; 6. 理解 Python 对象模型更动态,但工程代码仍应保持清晰约束。 ## 三、前置知识 - 已有 Java 类、对象、构造器、实例字段和 `static` 成员经验; - 已学习 Python 函数、字典和类型注解; - 能在 PowerShell 中运行 Python 文件。 ## 四、先看差异速查表 | 关注点 | Java | Python | |---|---|---| | 定义类 | `public class Agent` | `class Agent:` | | 创建对象 | `new Agent(...)` | `Agent(...)`,没有 `new` | | 初始化入口 | 与类同名的构造器 | `__init__` 特殊方法 | | 当前对象 | 隐式 `this` | 定义方法时显式写 `self` | | 实例字段 | 通常在类体中声明 | 通常在 `__init__` 中赋值创建 | | 类级数据 | `static` 字段 | 类属性 | | 实例方法 | 默认接收隐式 `this` | 第一个参数通常写 `self` | | 类级工厂 | 常用 `static` 方法 | 常用 `@classmethod` 和 `cls` | | 静态工具方法 | `static` 方法 | `@staticmethod`,无 `self`/`cls` | | 访问控制 | `public/protected/private` | 主要依赖命名约定,后续课详讲 | | 方法重载 | 支持同名不同参数 | 不按 Java 方式支持,后续课详讲 | ## 五、最小类定义 ```python class Agent: def __init__(self, name: str) -> None: self.name = name def describe(self) -> str: return f"Agent:{self.name}" agent = Agent("代码助手") print(agent.describe()) ``` 对应 Java 思维可以理解为: - `__init__` 负责接收初始化数据,但它不是与类同名的构造器; - `self.name = name` 在当前实例上创建并赋值 `name` 属性; - `self` 类似 `this`,只是 Python 要在方法定义中明确写出来; - 调用 `agent.describe()` 时不传 `self`,解释器会自动传入 `agent`; - 创建对象直接调用 `Agent(...)`,不写 `new`。 严格来说,Python 先通过 `__new__` 创建对象,再由 `__init__` 初始化对象。本阶段日常业务代码先掌握 `__init__` 即可,暂不展开 `__new__`。 ## 六、Python 属性不要求预先声明 Java 通常先声明字段: ```java private String name; ``` Python 常在 `__init__` 中直接建立实例属性: ```python self.name = name ``` Python 甚至允许对象创建后动态添加属性: ```python agent.status = "工作中" ``` 这体现了 Python 的动态性。它不是鼓励随意改变对象结构;在成熟项目中,仍应尽量在 `__init__` 中集中建立稳定的实例属性,让阅读者和类型检查工具更容易理解对象。 ## 七、实例属性与类属性 ```python class Agent: platform = "Codex" def __init__(self, name: str) -> None: self.name = name ``` - `self.name` 是实例属性,每个对象可以保存不同值; - `Agent.platform` 是类属性,由类统一提供,作用类似 Java `static` 字段; - `agent.platform` 也能读取类属性,但类级含义明确时优先使用 `Agent.platform`; - 如果执行 `agent.platform = "其他平台"`,通常会在该实例上创建同名实例属性,从而遮蔽类属性,而不是修改整个类的值。 最后一点与 Java 字段访问直觉不同,是常见问题。 ## 八、三种方法 ### 8.1 实例方法 ```python def describe(self) -> str: return self.name ``` 通过 `self` 访问当前对象,适合处理实例状态。 ### 8.2 类方法 ```python @classmethod def from_config(cls, config: dict[str, str]) -> "Agent": return cls(config["name"]) ``` 装饰器(Decorator)`@classmethod` 会让方法接收当前类 `cls`。它很适合命名工厂:使用 `cls(...)` 而非写死 `Agent(...)`,子类继承时更自然。 ### 8.3 静态方法 ```python @staticmethod def is_valid_name(name: str) -> bool: return bool(name.strip()) ``` 静态方法不接收 `self` 或 `cls`。它只是逻辑上属于该类,但不需要访问实例状态或类状态。 不要为了模仿 Java 工具类而大量使用静态方法。普通模块函数也是 Python 中很自然的组织方式。 ## 九、完整示例 示例文件: ```text 03_面向对象/3_1_Python与Java的类和对象/class_object_comparison.py ``` 示例会直接输出五项差异: 1. `__init__` 与 Java 构造器的区别; 2. `self` 与 `this` 的区别; 3. Python 属性的动态创建; 4. 类属性与 `static` 字段的对应关系; 5. Python 创建对象不写 `new`。 ## 十、运行方法 在项目根目录 `D:\Code\Python` 打开 PowerShell,运行: ```powershell python .\03_面向对象\3_1_Python与Java的类和对象\class_object_comparison.py ``` 完成练习后运行: ```powershell python .\03_面向对象\3_1_Python与Java的类和对象\practice.py ``` ## 十一、示例预期结果 输出中应依次看到“差异 1”到“差异 5”,并看到: ```text 代码助手 使用 gpt-5.2,运行平台:Codex 动态添加的 status:工作中 通过类读取:Codex;通过实例读取:Codex 审查助手 使用 gpt-5,运行平台:Codex 名称是否合法:True ``` ## 十二、Java 开发者常见误区 ### 12.1 创建对象时写 `new` Python 没有这种语法,直接写: ```python agent = Agent("代码助手") ``` ### 12.2 调用方法时手动传 `self` 调用 `agent.describe()` 时 Python 会自动绑定实例,不要写 `agent.describe(agent)`。 ### 12.3 忘记在方法定义中写 `self` 实例方法定义必须显式接收当前对象: ```python def describe(self) -> str: ... ``` `self` 不是保留关键字,但它是整个 Python 社区遵守的标准命名,不要改成其他名字。 ### 12.4 把所有工厂方法都写成静态方法 如果方法需要创建“当前类”的实例,优先考虑 `@classmethod` 和 `cls(...)`,这比写死类名更适合继承。 ### 12.5 认为没有 `private` 就无法封装 Python 更依赖约定、属性和接口设计。单下划线、双下划线及 `property` 会在下一课结合 Java 访问控制集中讲解。 ### 12.6 用多个同名方法实现重载 Python 类体中后定义的同名方法会覆盖前一个定义,不能照搬 Java 重载。常见替代方式包括默认参数、关键字参数和单分派,后续按需讲解。 ## 十三、课堂练习 打开 `practice.py`,完成一个 `Book` 类。练习覆盖: 1. `__init__` 和实例属性; 2. `self` 和实例方法; 3. 类属性; 4. `@classmethod` 命名工厂; 5. `@staticmethod` 校验方法; 6. 不使用 `new` 创建并运行对象。 每部分都附有 Java 对照提醒和最终预期输出,因此可以直接从练习开始。 ## 十四、参考答案 参考答案暂不写入 `practice.py`,避免直接照抄。完成后可以把代码交给我,我会从以下三方面反馈: 1. 正确性:对象创建、属性和方法结果是否正确; 2. 可读性:命名、结构和注释是否符合 Python 习惯; 3. 知识掌握:是否仍残留 Java 式写法,能否解释选择原因。 ## 十五、本课小结 - Python 用 `class` 定义类,但通常不写 `public`; - 使用 `__init__` 初始化实例,创建对象时不写 `new`; - `self` 类似 Java `this`,在定义实例方法时必须显式声明; - 实例属性通常通过 `self.xxx` 在 `__init__` 中建立; - 类属性类似 `static` 字段,但实例同名赋值会产生遮蔽; - `@classmethod` 接收 `cls`,适合可继承的命名工厂; - `@staticmethod` 不接收实例或类,只处理与类有关的独立逻辑; - Python 更动态,但成熟项目仍需要稳定、清晰的对象结构。 ## 十六、验收标准 - 能不使用 `new` 创建 Python 对象; - 能正确编写 `__init__` 和 `self`; - 能解释 `self` 与 Java `this` 的语法差异; - 能区分实例属性和类属性; - 能区分实例方法、类方法和静态方法; - 能解释为什么命名工厂中优先使用 `cls(...)`; - 能指出 Python 不支持 Java 式同名方法重载; - `practice.py` 的实际输出与题目预期一致。