Files
PythonLearn/03_面向对象/3_3_Python与Java的继承和多态差异/README.md

243 lines
7.7 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-3 课Python 与 Java 的继承和多态差异
## 一、本课定位
你已经掌握 Java 的继承、接口和多态,因此本课不重复通用理论,重点建立以下 Python 差异:
- `super()` 不需要显式写父类名;
- Python 没有强制的 `@Override`
- Python 支持多继承,并使用方法解析顺序决定查找路径;
- 多态调用经常基于鸭子类型,不要求对象继承共同父类或显式实现接口。
## 二、本课目标
完成本课后,你将能够:
1. 定义父类和子类并重写方法;
2. 使用 `super()` 复用继承链中的实现;
3. 解释方法解析顺序Method Resolution OrderMRO
4. 使用鸭子类型Duck Typing完成统一调用
5. 理解 Python 多继承和 Java 单类继承的差异;
6. 避免通过大量 `isinstance()` 分支模拟多态。
## 三、差异速查表
| 关注点 | Java | Python |
|---|---|---|
| 继承类 | `extends` | `class Child(Parent):` |
| 调用继承逻辑 | `super(...)` | 常用 `super()` |
| 重写标记 | 推荐 `@Override` | 没有强制对应标记 |
| 类继承数量 | 单继承 | 支持多继承 |
| 方法查找 | 类继承链 | 按 MRO 查找 |
| 接口多态 | 通常显式 `implements` | 常见鸭子类型,无需声明接口 |
| 类型判断 | 编译期和运行时类型体系 | 运行时更关注对象是否提供所需行为 |
## 四、继承和方法重写
```python
class Notifier:
def send(self, message: str) -> None:
raise NotImplementedError("子类必须实现 send() 方法。")
class EmailNotifier(Notifier):
def send(self, message: str) -> None:
print(f"发送邮件:{message}")
```
Python 子类把父类写在类名后的圆括号中,不使用 `extends`。子类定义同名方法即可重写,语言本身不要求添加 `@Override`
缺少 `@Override` 也意味着方法名拼错时不会获得同样的编译期保护,因此测试和静态检查更加重要。
## 五、正确理解 super()
```python
class EmailNotifier(Notifier):
def __init__(self, sender: str, address: str) -> None:
super().__init__(sender)
self.address = address
```
Python 3 通常直接写 `super()`,不需要传入当前类和 `self`
在简单单继承中,可以暂时把它理解为调用父类实现。但更准确的理解是:`super()` 会沿当前类的方法解析顺序继续查找方法。这个区别在多继承中尤其重要。
不要写死父类名称:
```python
Notifier.__init__(self, sender)
```
写死类名可能破坏多继承中的协作调用链。
## 六、方法解析顺序 MRO
Python 支持一个类继承多个父类:
```python
class LoggedEmailNotifier(EmailNotifier, LogMixin):
pass
```
可以查看查找顺序:
```python
print(LoggedEmailNotifier.mro())
```
当调用某个方法时Python 会按 MRO 中的顺序寻找第一个匹配实现。日常代码应避免设计过于复杂的多继承层次。
常见的安全用途是混入类Mixin它只提供一项小而明确的可复用能力通常不独立代表完整业务对象。
## 七、鸭子类型
经典描述是:“如果一个对象走起来像鸭子、叫起来像鸭子,就把它当作鸭子使用。”
在代码中,这表示调用者更关心对象是否提供所需行为:
```python
def send_message(notifier, message: str) -> None:
notifier.send(message)
```
下面两个类不必拥有共同父类:
```python
class EmailNotifier:
def send(self, message: str) -> None:
...
class ConsoleNotifier:
def send(self, message: str) -> None:
...
```
只要对象具有可调用的 `send()` 方法,`send_message()` 就可以使用它。
与 Java 接口多态相比:
- Java 常在编译期要求对象声明实现某个接口;
- Python 运行时可以直接尝试调用所需方法;
- Python 也能使用协议Protocol为鸭子类型提供静态检查后续结合类型设计再展开。
## 八、不要用类型分支代替多态
不推荐:
```python
if isinstance(processor, CardProcessor):
...
elif isinstance(processor, QrCodeProcessor):
...
```
推荐让每个对象自己实现统一行为:
```python
processor.pay(amount)
```
这样新增处理器时,调用方通常不需要修改。
`isinstance()` 本身并非错误;当业务规则确实依赖类型时可以使用。但如果只是为了选择同一操作的不同实现,通常应优先使用多态。
## 九、继承还是组合
Java 与 Python 都不应为了复用少量代码而滥用继承。
- 真正存在“是一个”关系,并需要替换父类型行为时,可以考虑继承;
- 只是“拥有一个”协作者或需要灵活替换能力时,优先考虑组合;
- 只需要调用统一行为时Python 的鸭子类型可能已经足够。
组合会在后续业务模型课程中结合实例继续练习。
## 十、完整示例和运行方法
示例文件:
```text
03_面向对象/3_3_Python与Java的继承和多态差异/inheritance_polymorphism_comparison.py
```
在项目根目录运行:
```powershell
python .\03_面向对象\3_3_Python与Java的继承和多态差异\inheritance_polymorphism_comparison.py
```
示例输出会直接展示:
1. `super()` 复用继承逻辑;
2. 方法重写;
3. 没有共同父类的鸭子类型调用;
4. 多继承类的 MRO。
## 十一、常见错误
### 11.1 把 super() 当成固定父类对象
它实际按照 MRO 继续查找。在多继承中,下一站不一定是你凭直觉认定的某个父类。
### 11.2 忘记调用父类初始化逻辑
如果父类负责建立必要属性,子类通常需要调用 `super().__init__(...)`,否则使用父类属性时可能出现 `AttributeError`
### 11.3 认为多态必须继承共同父类
Python 鸭子类型只要求对象提供当前操作需要的方法。
### 11.4 滥用多继承
多继承会增加 MRO 和初始化协作的理解成本。优先使用清晰的小型 Mixin复杂业务能力通常更适合组合。
### 11.5 假设方法已经被正确重写
Python 没有强制 `@Override`。方法名或参数写错时,应通过测试、类型检查或代码审查发现。
## 十二、课堂练习
打开:
```text
03_面向对象/3_3_Python与Java的继承和多态差异/practice.py
```
完成支付处理器练习:
1. 定义父类统一操作;
2. 子类通过 `super()` 复用初始化;
3. 未继承父类的二维码处理器参与统一调用;
4. 使用一个函数调用两种处理器;
5. 输出并观察 `CardProcessor.mro()`
## 十三、参考答案与验证标准
参考答案暂不写入练习文件。完成后按以下层级验证:
- 必须修复:语法错误、运行错误、支付结果错误、没有使用 `super()`、通过具体类型分支实现调用;
- 建议改进:重复代码、职责混乱、明显影响理解的命名;
- 可选优化:非必要类型注解、输出美化和不影响行为的格式问题。
只有“必须修复”会阻止课程验收。
## 十四、本课小结
- Python 使用 `class Child(Parent)` 表示继承;
- 子类定义同名方法即可重写,没有强制 `@Override`
- `super()` 会沿 MRO 继续查找实现;
- Python 支持多继承,但复杂多继承应谨慎使用;
- 鸭子类型关注对象提供的行为,不要求共同父类或显式接口;
- 统一调用不应依赖大量具体类型判断;
- 继承用于真正的类型关系,单纯复用通常优先考虑组合。
## 十五、验收标准
- 能正确使用 `super().__init__()`
- 能重写父类方法;
- 能输出并解释简单类的 MRO
- 能让未继承共同父类的对象参与统一调用;
- 能解释鸭子类型与 Java 接口多态的区别;
- 能避免通过 `isinstance()` 分支实现本题多态;
- `practice.py` 输出正确支付结果。