feat(面向对象): 完成第三阶段差异化课程

This commit is contained in:
zhiye.sun
2026-08-11 17:53:53 +08:00
parent b348dc0a1f
commit f1d9548646
16 changed files with 2131 additions and 14 deletions

View File

@@ -0,0 +1,259 @@
# 第 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` 的实际输出与题目预期一致。

View File

@@ -0,0 +1,63 @@
# 第 3-1 课完整示例Python 与 Java 的类和对象
#
# 本示例不重复讲解通用的面向对象概念,而是集中展示 Python 与 Java 的写法差异。
# 运行程序时,每一段输出都会再次提醒对应差异,便于只看代码和结果也能掌握重点。
class Agent:
"""表示一个简单的 Agent并演示 Python 类的核心语法。"""
# Python 的类属性类似 Java 的 static 字段,由所有实例共同访问。
platform = "Codex"
def __init__(self, name: str, model: str = "gpt-5") -> None:
"""初始化实例属性__init__ 的作用接近 Java 构造器,但它不是类名同名方法。"""
# Python 通常直接在 __init__ 中创建实例属性,不需要提前声明字段。
# self 类似 Java 的 this但在实例方法定义中必须显式写出来。
self.name = name
self.model = model
def describe(self) -> str:
"""返回当前对象的说明文字。"""
return f"{self.name} 使用 {self.model},运行平台:{self.platform}"
@classmethod
def from_config(cls, config: dict[str, str]) -> "Agent":
"""通过配置创建对象cls 指向当前类,作用类似可继承的命名工厂。"""
return cls(config["name"], config.get("model", "gpt-5"))
@staticmethod
def is_valid_name(name: str) -> bool:
"""校验名称;该方法不需要访问某个对象或当前类。"""
return bool(name.strip())
def main() -> None:
"""创建对象并输出 Python 与 Java 的关键差异。"""
code_agent = Agent("代码助手", "gpt-5.2")
review_agent = Agent.from_config({"name": "审查助手"})
print("差异 1Python 使用 __init__ 初始化对象,不写与类同名的构造器。")
print(code_agent.describe())
print()
print("差异 2self 类似 Java 的 this但定义实例方法时必须显式声明。")
print(f"通过对象访问实例属性:{code_agent.name}")
print()
print("差异 3实例属性通常在运行时创建不需要像 Java 字段那样预先声明。")
code_agent.status = "工作中"
print(f"动态添加的 status{code_agent.status}")
print()
print("差异 4类属性类似 static 字段,但也能通过实例读取。")
print(f"通过类读取:{Agent.platform};通过实例读取:{review_agent.platform}")
print()
print("差异 5Python 不使用 new直接调用类即可创建对象。")
print(review_agent.describe())
print(f"名称是否合法:{Agent.is_valid_name(review_agent.name)}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,82 @@
# 第 3-1 课课堂练习Python 与 Java 的类和对象
#
# 这份练习把关键差异直接写进题目,不要求先完整阅读讲义。
# 请先独立完成;不要删除题目、预期输出和自查说明。
# 第一部分:定义 Book 类
# 1. 不要像 Java 一样预先声明 title、author、price 字段。
# 2. 定义 __init__(self, title, author, price=0.0) 方法。
# 3. 为 title 和 author 添加 str 类型注解,为 price 添加 float 类型注解。
# 4. 返回值标注为 None。
# 5. 在 __init__ 中通过 self.title、self.author、self.price 创建实例属性。
#
# Java 对照提醒:
# - Python 的 __init__ 承担初始化职责,但不是“与类同名的构造器”;
# - self 类似 Java 的 this但必须写在方法参数列表中
# - 创建对象时不写 new也不手动传入 self。
# 第二部分:定义实例方法 get_summary(self)
# 1. 返回值标注为 str。
# 2. 返回格式书名《Python 后端开发》作者小明价格59.0 元。
# 3. 必须通过 self 读取三个实例属性。
#
# Java 对照提醒:
# - Python 方法没有 public 关键字;
# - 调用时写 book.get_summary()Python 会自动把 book 传给 self。
# 第三部分:添加类属性 category
# 1. 在 Book 类中、所有方法外定义 category = "编程"。
# 2. 在后面的 main() 中分别使用 Book.category 和 book.category 输出它。
#
# Java 对照提醒:
# - 类属性在用途上类似 Java 的 static 字段;
# - 为避免实例属性遮蔽类属性,本题不要执行 book.category = ...。
# 第四部分:定义类方法 from_text(cls, text)
# 1. 在方法上方添加 @classmethod。
# 2. text 的格式固定为“书名|作者”,例如“流畅的 Python|Luciano”。
# 3. 使用 text.split("|") 得到书名和作者。
# 4. 使用 cls(title, author) 创建并返回对象,让 price 使用默认值。
#
# Java 对照提醒:
# - cls 指向当前类classmethod 常用于实现命名工厂;
# - 不要把类名 Book 写死在 return 中,使用 cls 更利于继承。
# 第五部分:定义静态方法 is_valid_price(price)
# 1. 在方法上方添加 @staticmethod。
# 2. price 标注为 float返回值标注为 bool。
# 3. price 大于或等于 0 时返回 True否则返回 False。
#
# Java 对照提醒:
# - staticmethod 不接收 self 或 cls
# - 当逻辑与 Book 有关、但不需要对象状态和类状态时,可以使用它。
# 第六部分:定义 main() 并完成运行验证
# 1. 使用 Book("Python 后端开发", "小明", 59.0) 创建 book注意不写 new。
# 2. 输出 book.get_summary()。
# 3. 分别通过类和对象输出 category。
# 4. 使用 Book.from_text("流畅的 Python|Luciano") 创建 imported_book。
# 5. 输出 imported_book.get_summary(),其价格应为默认值 0.0。
# 6. 输出 Book.is_valid_price(59.0) 和 Book.is_valid_price(-1.0)。
# 7. 添加程序入口判断,直接运行本文件时调用 main()。
#
# 预期输出:
# 书名《Python 后端开发》作者小明价格59.0 元
# 通过类读取分类:编程
# 通过对象读取分类:编程
# 书名:《流畅的 Python》作者Luciano价格0.0 元
# 59.0 是合法价格True
# -1.0 是合法价格False
# 完成后自查:
# 1. 是否能解释 self 与 Java this 的相同点和写法差异;
# 2. 是否记住 Python 创建对象时不写 new
# 3. 是否知道 __init__ 负责初始化,而不是 Java 式的同名构造器;
# 4. 是否能区分实例属性、类属性、实例方法、类方法和静态方法;
# 5. 是否没有在类外直接替学习者写出答案。