Files
PythonLearn/03_面向对象/3_1_Python与Java的类和对象/README.md

260 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第 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` 的实际输出与题目预期一致。