Spark等开源项目频现重载误区 静态检查难断运行时布尔值

【python】终于有人讲怎么用overload了 — 码农高天

码农高天

Python 静态类型系统无法在运行前获知具体变量值,若需依据布尔状态切换函数返回类型,必须使用 Literal 字面量显式约束重载分支。

AI-assisted summary of 【python】终于有人讲怎么用overload了 — original source. Check the original for full context.

Source published:

Key ideas

  • 针对联合类型联合输入导致的输出类型模糊问题,`@overload` 装饰器通过显式声明多组输入输出分支,让 mypy 与 VS Code 实现精准的静态类型推断。 (0:00)
  • 使用 `@overload` 时,被装饰的空函数体桩代码必须与实际实现同名,且中间绝不能插入其他函数定义;若用于类方法,`@overload` 必须置于 `@classmethod` 外层。 (0:00)
  • Python 静态类型系统无法在编译/静态分析期获知运行时的具体变量值,诸如 `allow_none: bool = False` 的写法无法实现根据入参值切换返回类型的预期。 (5:00)
  • 若要根据布尔值或常量标识改变返回值类型,正确方案是使用 `typing.Literal` 分别声明 `Literal[True]` 与 `Literal[False]` 分支。 (5:00)

Detailed notes

## Background 该视频由“码农高天”于2026年3月22日发布在YouTube平台。视频主要围绕Python标准库中的 `@overload` 装饰器展开,旨在帮助开发者更准确地编写函数类型提示,并剖析实际使用中常见的思维陷阱。

## TL;DR 视频通过简单的加倍函数与复杂的实体查询业务场景,系统解析了Python类型系统中 `@overload` 装饰器的核心价值与规范写法。码农高天指出,普通联合类型无法表达入参和出参的严格对应关系,而 `@overload` 能显式定义不同参数组合的返回分支。针对参数增多时的API设计,他提醒开发者在考虑透传包装时要穷尽用例。最后,视频深入剖析了开源项目中误将运行时布尔值与静态类型混淆的常见陷阱,并给出了利用 `typing.Literal` 精准推导返回类型的正确解法。

## Atmosphere & style 整期视频呈现出资深工程师特有的严谨与通透,讲解风格条理分明、直击痛点。主讲人以清晰的代码推演逐步剥开类型检查器的底层逻辑,语调从容自如,在指出Spark等大型开源项目中普遍存在的认知误区时犀利且具说服力。观众在跟随其步步排查报错的过程中,能真切体会到从“直觉试错”升级为“理解静态分析本质”的技术顿悟感。

## Bulletin - [00:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=0s) 针对联合类型联合输入导致的输出类型模糊问题,`@overload` 装饰器通过显式声明多组输入输出分支,让 mypy 与 VS Code 实现精准的静态类型推断。 - [00:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=0s) 使用 `@overload` 时,被装饰的空函数体桩代码必须与实际实现同名,且中间绝不能插入其他函数定义;若用于类方法,`@overload` 必须置于 `@classmethod` 外层。 - [05:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=300s) Python 静态类型系统无法在编译/静态分析期获知运行时的具体变量值,诸如 `allow_none: bool = False` 的写法无法实现根据入参值切换返回类型的预期。 - [05:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=300s) 若要根据布尔值或常量标识改变返回值类型,正确方案是使用 `typing.Literal` 分别声明 `Literal[True]` 与 `Literal[False]` 分支。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=0s) 普通联合类型(如 `int | str`)无法建立输入与返回类型的对应绑定,容易导致 mypy 报错,而 `@overload` 能显式定义这些类型组合。 - [00:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=0s) `@overload` 修饰的函数体应保持为空(使用 `...` 或 `pass`),且其真实的实现代码必须紧随其后,中间禁止插入无关函数。 - [00:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=0s) 相比于 `TypeVar` 仅适用于输入输出同构的泛型绑定,`@overload` 能够处理输入与输出非对称映射的复杂业务场景。 - [02:30](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=150s) 在类方法上使用 `@overload` 时,装饰器顺序必须将 `@overload` 放置在 `@classmethod` 的上方。 - [02:30](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=150s) `@overload` 不仅可以区分参数类型,还可以根据传入参数的数量及不同组合定义不同的返回类型推断。 - [02:30](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=150s) 设计重载签名需要权衡接口定位:面向最终用户的 API 可以收紧参数组合,但作为内部包装函数(Wrapper)调用的接口则需穷尽透传参数的可能性。 - [05:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=300s) 静态类型检查器只负责分析类型,无法且不可能依据动态运行时的具体变量值(Runtime Value)来推导返回类型。 - [05:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=300s) 在重载桩代码中写 `allow_none: bool = False` 仅表示类型为布尔且默认值为 False,无法表达“当传入 False 时”的类型分支分发。 - [05:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=300s) 对于必须依据布尔或固定常量区分返回类型的业务需求,应采用 `typing.Literal` 将静态字面量直接约束到签名中。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=0s) 为什么需要 `@overload` 以及基本使用规范

在 Python 类型提示(Type Hints)中,很多函数的输入类型与输出类型之间存在明确的对应关系。码农高天首先给出了一个直观的例子:编写一个通用的 `double` 函数,接收的参数既可以是 `int` 也可以是 `str`,在函数内部统一执行 `return x * 2`。如果只使用简单的联合类型提示(Union Type),将输入标注为 `int | str`,输出也标注为 `int | str`,那么当主程序调用 `y = double(5)` 并随后执行 `print(y + 3)` 时,静态类型检查工具 mypy 就会报错。mypy 报错的原因在于它无法支持 `str` 与 `int` 的直接相加,而在 mypy 的静态视角里,返回变量 `y` 的类型被推断为可能是 `int` 也可能是 `str`。

```mermaid flowchart TD accTitle: Overload 类型分支匹配流程 accDescr: 展示调用 double 函数时如何根据输入类型匹配不同的 overload stub 分支并推断输出类型

subgraph Stubs["Overload Stubs (空函数体定义)"] S1["@overload<br/>def double(x: int) -> int:..."] S2["@overload<br/>def double(x: str) -> str:..."] end

subgraph Impl["真实实现 (Implementation)"] REAL["def double(x: int | str) -> int | str:<br/>return x * 2"] end

CALL1["调用: double(5)"] -->|"输入匹配 int"| S1 S1 -->|"推断返回 int"| OUT1["推断类型: int (y + 3 合法)"]

CALL2["调用: double('abc')"] -->|"输入匹配 str"| S2 S2 -->|"推断返回 str"| OUT2["推断类型: str"]

Stubs -.->|"紧跟真实实现"| Impl ```

虽然编写代码的人明确知道“输入是 `int` 则输出必定是 `int`,输入是 `str` 则输出必定是 `str`”,但普通的联合类型标注无法向类型系统传递这种对应关系。为了解决这个问题,Python 标准库的 `typing` 模块提供了 `@overload` 装饰器。码农高天解释道:“Overload的功能简单来说就是把你这个函数不同的类型组合给显示的列出来。”通过显式声明两个分离的签名——一个输入 `int` 返回 `int`,另一个输入 `str` 返回 `str`——VS Code 的类型推断和 mypy 便能精准得知 `double(5)` 的结果必然是 `int`,从而顺利通过类型检查。

在使用 `@overload` 时,开发者必须严格遵守几项结构规范: - 被 `@overload` 装饰的函数签名必须与最终实现的函数名称完全一致,这样 Python 与类型检查器才知道是在为哪一个函数定义重载分支。 - 重载定义本质上只是桩代码(Stub),其函数体应当保持为空(例如直接写 `...` 或 `pass`)。 - 真实的函数实现代码必须紧紧跟在所有 `@overload` 签名下方,中间绝不允许插入任何其他函数定义。例如在桩签名与实现之间插播一个 `triple` 函数,mypy 就会立即报错。

面对一些熟悉进阶类型特性的观众可能提出的“为什么不直接用 `TypeVar` 泛型”的疑问,码农高天承认:“在这个函数下 type var 确实是更好的解决方案但是overload本身是一个更普适的方法它可以解决更多的问题”。因为 `TypeVar` 适用于“输入什么就原样输出什么”的直接绑定场景,但在现实业务中,输入与输出往往并非同构。

---

[02:30](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=150s) 业务场景中的 `@overload` 与多参数包装陷阱

为了展示 `@overload` 的普适性,码农高天举出了一个不能用 `TypeVar` 解决的业务例子:一个包含 `name` 和 `age` 属性的 `Person` 类,以及一个根据数据库检索年龄的函数 `get_age`。该函数的参数既可以直接接收一个 `Person` 对象,也可以接收表示人名的 `str`。如果传入的是 `Person` 对象,则直接返回其实际的 `age`(类型必然是 `int`);如果传入的是 `str`,则去数据库查询,查到返回 `int`,查不到则返回 `None`。在此场景下,输入为 `Person` 时输出必为 `int`,输入为 `str` 时输出则为 `int | None`,两者的输出分支是不对称的,`TypeVar` 无法直接映射这种关系。使用 `@overload` 则能优雅地分别列出这两组逻辑签名。此外码农高天顺带补充道,如果该方法是一个类方法(`@classmethod`),需要将 `@overload` 装饰器写在 `@classmethod` 的上方。

```mermaid flowchart LR accTitle: get_age 函数的多重载签名决策 accDescr: 展示 get_age 在输入 Person、str 以及 default_age 组合下的类型重载覆盖

subgraph Inputs["输入参数组合"] P1["person: Person, default_age: 省略"] P2["person: str, default_age: 省略"] P3["person: Person | str, default_age: int"] P4["person: Person | str, default_age: None"] end

subgraph Returns["推断输出类型"] R1["int"] R2["int | None"] R3["int"] R4["int | None"] end

P1 --> R1 P2 --> R2 P3 --> R3 P4 --> R4 ```

除了根据输入类型进行分发,`@overload` 还可以依据传入参数的数量进行分支区分。当向 `get_age` 添加一个可选参数 `default_age`(未找到对应人员时返回默认年龄)时,重载设计的复杂度显著提高: - 若未提供 `default_age`,行为维持原有分支(`Person` 返回 `int`,`str` 返回 `int | None`)。 - 若显式提供了一个整数类型的 `default_age`(如 `int`),由于有兜底值保障,返回值必然是 `int`。

但此时往往会出现重载设计的疏漏。例如开发者可能只考虑了 `person: str` 搭配 `default_age: int` 的情况,却漏掉了调用方同时传入 `Person` 对象与 `default_age` 的可能。如果在类型签名中遗漏了该分支,mypy 就会在用户传入 `Person` 和 `default_age` 时报告签名不匹配的错误。

码农高天指出,这种报错不一定是代码层面的 Bug,而是一个设计决策(Design Decision)——即API作者是否希望用户这样调用:“你可以说如果你输入的是一个person那么他就不应该有defaultage但是你在想use case的时候你一定要想到这个函数是不是一定会直接对着用户的使用接口”。在真实软件工程中,函数往往会被外部的 Wrapper(包装函数)层层封装,透传各种参数,经常会产生看似“不合常理”的参数组合。因此,更健壮的重载应当允许 `person` 无论传 `Person` 还是 `str`,只要 `default_age` 是 `int`,返回值就统一为 `int`。更进一步,如果 Wrapper 传递进来的 `default_age` 本身就是 `None`,重载系统还必须进一步补充 `default_age: None` 时返回 `int | None` 的分支,才能在工程实践中彻底杜绝静态类型检查报错。

- 类方法定义顺序:在方法上同时使用 `@overload` 与 `@classmethod` 时,应将 `@overload` 放置在外层(上方)。 - 接口定位与健壮性:作为面向最终终端用户的 API,严格限制非法参数组合是合理的;但作为内部函数(Internal Function)或常被 Wrapper 透传的接口,重载定义必须穷尽所有可能的透传组合。

---

[05:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=300s) 运行时的值与静态分析的冲突:常见陷阱与 `Literal` 解法

随后,码农高天深入剖析了一个在开源项目中极其普遍的错误模式:“这个问题我在 Spark 里面发现了好几处啊我还没有完全来得及改完”。假设给 `get_age` 增加一个布尔参数 `allow_none`(默认值为 `True`),当设置为 `False` 时,如果查不到年龄就会直接抛出异常(`raise Error`)而不是返回 `None`。开发者直觉上往往会有强烈的冲动去声明一个“当 `allow_none` 为 `False` 时绝对只返回 `int`”的重载。

许多人会写出错误的语法:例如在重载桩函数中将参数直接声明为 `allow_none: bool = False`,误以为类型系统能理解“当传入 `False` 时返回 `int`”。码农高天严厉指出了这种认知的谬误:“Python 的 type int system不会去根据输入变量的值来去给你判断类型就是整个这个python typing system没有这个能力他没办法说你这个输入是true的时候怎么样输入是false的时候怎么样这个写法就是纯错”。这种写法在语法上的真实含义仅仅是:参数类型是 `bool`,并且它的默认缺省值是 `False`,类型系统完全无法从中获知“调用者传递了布尔值 `False`”这一事实。

另一些人可能会尝试在两个重载中分别写出“有该参数”与“无该参数”,默认认为传入参数的人必然传的是 `False`。这同样站不住脚,因为上层 Wrapper 完全可能将外部传来的布尔变量直接原样透传,哪怕调用方显式传了一个与默认值相同的 `True`,这种推断也会瞬间失效。

```mermaid flowchart TD accTitle: 静态类型分析与 Literal 取值机制 accDescr: 对比常规 bool 参数在静态期无法推断,与 Literal 强约束在静态期精确推导类型的过程

subgraph WrongWay["运行时值判断 (静态分析无法实现)"] W1["x: bool (由运算得到)"] --> W2{"运行时到底是 True 还是 False?"} W2 -->|"静态期不可知"| W3["静态分析器无法判断具体分支"] end

subgraph RightWay["字面量 Literal 解法"] L1["allow_none: Literal[False]"] -->|"静态字面量明确为 False"| R_INT["推断返回值必为: int"] L2["allow_none: Literal[True]"] -->|"静态字面量明确为 True"| R_UNION["推断返回值可能为: int | None"] end ```

这种矛盾的本质在于静态类型分析与动态运行时值之间的边界:“事实上 Static Analysis 本来就不可能在依赖 runtime value 的时候来完成就是我这个值只有跑的时候才知道是多少我怎么可能在静态分析的时候给你分析明白呢这件事是不科学的对吧”。

如果业务确实存在“依据布尔标识符严格改变返回类型”的强需求,正确的类型提示工具是使用 `typing.Literal`: - 将重载签名的参数显式约束为 `Literal[False]`,此时返回类型指定为 `int`。 - 将另一个重载签名约束为 `Literal[True]`,此时返回类型指定为 `int | None`。

通过 `Literal` 机制,静态分析器不再处理模糊的 `bool` 变量,而是要求代码中必须显式传入字面量 `True` 或 `False`(而非运行时动态计算出的布尔变量)。在编辑器中测试可以清晰看到:当显式传入 `allow_none=True` 时,执行 `age + 1` 会被 mypy 标红报错(因为可能为 `None`);而当显式写入 `allow_none=False` 时,mypy 便能百分之百断定 `age` 是 `int`,从而不再报错。码农高天强调,`Literal` 方案并非歪门邪道,在很多场景下都有着极高的应用价值,也是理解 Python 类型系统中 `Literal[True, False]` 与泛指的 `bool` 之间核心差异的关键所在。

## Notable quotes > [05:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=300s) 事实上 Static Analysis 本来就不可能在依赖 runtime value 的时候来完成 — 码农高天

> [00:00](https://www.youtube.com/watch?v=njFDgzSBM0Q&t=0s) Overload的功能简单来说就是把你这个函数不同的类型组合给显示的列出来。 — 码农高天

## Mentioned 码农高天, Python, mypy, VS Code, Spark

## Video description 这期视频聊一下overload这个decorator,它可以让我们函数的类型提示更加准确。另外也来聊一下用overload的时候可能遇到的陷阱。

[Source](https://www.youtube.com/watch?v=njFDgzSBM0Q)

中文简报

Spark等开源项目频现重载误区 静态检查难断运行时布尔值 Python 静态类型系统无法在运行前获知具体变量值,若需依据布尔状态切换函数返回类型,必须使用 Literal 字面量显式约束重载分支。 视频通过简单的加倍函数与复杂的实体查询业务场景,系统解析了Python类型系统中 `@overload` 装饰器的核心价值与规范写法。码农高天指出,普通联合类型无法表达入参和出参的严格对应关系,而 `@overload` 能显式定义不同参数组合的返回分支。针对参数增多时的API设计,他提醒开发者在考虑透传包装时要穷尽用例。最后,视频深入剖析了开源项目中误将运行时布尔值与静态类型混淆的常见陷阱,并给出了利用 `typing.Literal` 精准推导返回类型的正确解法。 针对联合类型联合输入导致的输出类型模糊问题,`@overload` 装饰器通过显式声明多组输入输出分支,让 mypy 与 VS Code 实现精准的静态类型推断。 (0:00) 使用 `@overload` 时,被装饰的空函数体桩代码必须与实际实现同名,且中间绝不能插入其他函数定义;若用于类方法,`@overload` 必须置于 `@classmethod` 外层。 (0:00) Python 静态类型系统无法在编译/静态分析期获知运行时的具体变量值,诸如 `allow_none: bool = False` 的写法无法实现根据入参值切换返回类型的预期。 (5:00) 若要根据布尔值或常量标识改变返回值类型,正确方案是使用 `typing.Literal` 分别声明 `Literal[True]` 与 `Literal[False]` 分支。 (5:00)

Reading summary

码农高天在讲解中指出,Python 的 `@overload` 装饰器用于显式列出函数不同的输入与输出类型映射,以解决联合类型与泛型无法精准覆盖的类型检查问题,同时开发者需警惕静态类型推断与运行时值之间的边界陷阱。 解决联合类型局限与基本语法规范 (0:00) 在 Python 类型提示中,简单的联合类型(如 `int | str`)无法建立入参和出参的一对一绑定。例如在 `double(x: int | str) -> int | str` 的场景下,传入整数调用 `double(5)` 后与数字相加,mypy 会因无法排除返回值是 `str` 而报错。`@overload` 的核心作用是将函数所有合法的类型组合显式列出,使类型检查器能精确推导对应分支的返回类型。相比于仅适用于同构绑定的 `TypeVar`,`@overload` 更具普适性。 使用 `@overload` 时必须遵守严格的代码组织结构:重载函数名必须与实现函数名完全一致;重载声明属于空桩代码(Stub),其函数体通常仅写 `...` 或 `pass`;真正的业务实现必须紧随所有重载签名下方,中间不能穿插任何其他函数,否则 mypy 会直接报错。若方法带有 `@classmethod`,应将 `@overload` 装饰器放置在其外层。 业务不对称场景与包装层透传陷阱 (2:30) 在实际业务中,输入与输出往往是不对称的,例如通过 `get_age` 函数查询人员年龄:传入 `Person` 对象时必返回 `int`,而传入姓名 `str` 查询数据库时可能返回 `int | None`。这种非同构映射无法通过泛型解决,但可以通过多个 `@overload` 分支优雅表达。 当函数引入可选兜底参数(如 `default_age`)时,签名设计容易出现疏漏。开发者可能仅针对 `str` 搭配 `int` 默认值做了重载,却忽略了调用方同时传入 `Person` 对象与默认值的可能。码农高天强调,面向终端用户的接口可以严格限制调用方式,但若作为常被外层 Wrapper 封装或透传参数的内部函数,设计者必须穷尽各种透传组合(包括 `default_age: None` 的分支),否则会导致上层调用方出现静态类型检查报错。 静态分析与运行时值的冲突及 Literal 解法 (5:00) 针对业务中常见的依据布尔标识改变返回类型的需求(例如设置 `allow_none=False` 时查不到就抛出异常、必返回 `int`),部分开源项目(如 Spark 中被发现的几处代码)存在认知误区。在重载桩函数中书写 `allow_none: bool = False` 并不能让类型系统理解“传入了布尔值 False”,该语法在静态视角下仅代表参数类型为 `bool` 且缺省值为 `False`。静态分析无法在依赖动态运行时变量值的前提下做出推导。 正确的解决方案是引入 `typing.Literal`。通过将重载签名分别约束为 `allow_none: Literal[False]` 返回 `int`,以及 `allow_none: Literal[True]` 返回 `int | None`,静态分析器便能在调用方显式写入布尔字面量时进行确定性推断。这消除了 `bool` 抽象类型带来的歧义,使静态分析能够安全通过。 掌握 `@overload` 的关键在于分清静态分析与动态运行时的边界,根据 API 的使用场景(终端接口或内部包装透传)补全所有潜在的签名分支,并借助 `Literal` 处理字面量强约束推断。

Source quotes

[5:00] 码农高天: 事实上 Static Analysis 本来就不可能在依赖 runtime value 的时候来完成 [0:00] 码农高天: Overload的功能简单来说就是把你这个函数不同的类型组合给显示的列出来。

Original transcript

[0:00] 我们今天来讲一个和 Python type hint 有关的 decorator我们先来看左边的程序啊随便写的这是一个 double 函数啊进来这个值可以是 int 或者是 string不管进来的是什么我都给它乘2然后我把这个值返回回去那我们在做type in的时候很明显这个输入可以是int或者string输出也是int或者string那我的主程序就写y=w5然后print y+3这个时候如果你用Mipi去检查,他就会报错它会告诉你第九行这里有问题我们不支持Stream和Int的加法因为在mypi的视角里这个Y有可能是int也有可能是string所以Y+3就有可能不被支持可是我们实现的时候我们其实知道如果输入是int那输出就是int输入是string输出就是string所以这个y等于double5y必然是一个int那我们如何在python的typeint系统里面表示一件事呢这就是我们今天的主题啊typing里的overload decoratorOverload的功能简单来说就是把你这个函数不同的类型组合给显示的列出来对于这个函数它就是这样的我们把它的两种使用方法分别拿出来输入是int 输出是int 输出是string 输出是string这个时候我们看 vs code已经能知道这个 y 一定是个int了因为我们的输入值是一个int同时买派也可以pass了那使用overload的时候有几个要求首先,overload下面这个函数的definition,它的名字必须和你下面实现的那个函数一致这样python才会知道你在overload的函数这个overload的函数应该就是一个stub 也就是说它的函数体应该是空的然后这个函数本身的实现一定要紧跟在它的overload下面中间不能有任何其他的函数比如我这里写了一个triple这个时候买派就会报错了好 那这个时候肯定会有知识比较丰富的小伙伴说我觉得你这个解决方法不优雅这个地方应该用type var 对不对因为你这个函数本质上是输出的类型和输入的类型有某一种关系更直白一点就是输入什么类型输出什么类型这个观点没错啊在这个函数下 type 1 确实是更好的解决方案但是overload本身是一个更普适的方法它可以解决更多的问题我们来看这个例子啊也是我随手写的我们有一个person类 每个人有一个名字 有一个年龄然后假设把所有的person都放到了一个数据库里我们可以通过每一个人的名字来找有没有这个person如果没有的话就返回一个NUN接下来我们实现了一个函数叫getage我们输入可以是一个person类也可以是一个string如果它是一个类的话我就直接返回它的edge否则的话我就找一下有没有这个人有这个人返回edge没有返回null那大家这个时候再来看我们这个函数它其实也应该被overload因为不同的输入类型实际上是对应着不同的输出类型但这个时候type var就不好解决问题了因为输入类型和输出类型并不是直接产生联系的这个时候我们的overload就可以写成这个样子如果输入的是一个person类那返回的一定是一个int如果输入的是一个string那返回的可能是int 也可能是none这里稍微留外提一下如果你这个method是一个class method要把overload写到class method上面那overload除了可以区分不同的type,它还可以区分不同的参数数量。比如我们这里加了一个可选的参数,defaultage。然后如果没有找到这个person我们就返回这个defaultage那我们前两个overload实际上是可以保留的因为如果我们没有给defaultage他的behavior跟之前是一样的而我们可以加上如果我们给出了Default Edge 那么返回值必然是一个Int但是这里大家发现一个什么小问题了吗我这里只 overload了 person 是 string 的情况如果我这个函数的输入是一个person同时加了defaultage呢这里mypy就会报错因为我们压根没有设计这种输入方案那你说这里是一个错误吗不一定更多的是一个所谓Design Decision就是你吸不吸往用户这么使用的函数你可以说如果你输入的是一个person那么他就不应该有defaultage但是你在想use case的时候你一定要想到这个函数是不是一定会直接对着用户的使用接口如果用户自己写了一个wrapper这是一个很常见的情况对不对就我们把上面来的参数传到下面去在这种情况下我们很有可能就会拿到一些看起来好像不太成对的输入那很多时候我们可能还是需要去处理这种输入所以这个地方我们的overload可能更好的还是要写成person 既可以是person 也可以是street只要你的defaultage给了一个int 我的返回必然是一个int但是这样就够了吗我们再想一下刚才rapper的那个问题如果我们的refer [5:00] 这个样子那输入的这个default edge是不是有可能是一个num它依然是一个我们没有处理的情况就我们想的时候可能觉得我这个API在使用的时候如果你给一个default edge它必须得是一个int这个理解没有什么问题就像我刚才说的你把自己当成一个对用户最终端的API然后你说如果你调用我给了default h 就必须是一个integer 这件事是合理的在很多场景下它都有可能是对的但是如果你要再处理这个问题比如你自己是一个internal的函数你就是要处理输入是num的问题那你就还得再写一个overload啊如果defaulted是none的话啊那返回有可能是in 有可能是none这样我们就比较完整的给getH这个函数写了overload那除了 overload 应该怎么使用之外我在这里还想讲一个大家非常容易陷入的误区这个问题我在 Spark 里面发现了好几处啊我还没有完全来得及改完我们假设我给这个函数又加了一个argument叫allow none在默认的情况下它可能是true但是如果你把它设置成了false那当我们要return run的时候 我们就会raise一个error这个时候你可能会有很多想法对吧因为我们的输入值确实改变了返回值了如果allow none是false的话我们就不可能返回none了你可能会有这么写的冲动对吧就你想表达的是如果allow none这个数给的是false就肯定直返回 int但是这个写法是完全错误的啊Python 的 type int system不会去根据输入变量的值来去给你判断类型就是整个这个python typing system没有这个能力他没办法说你这个输入是true的时候怎么样输入是false的时候怎么样这个写法就是纯错这个写法的意思是allow none是一个不它的default value是false但实际上不是这样的不是这样的就是你想表达的东西和你写的东西是不一致的啊就这种写法在各种意义上都是不对的那可能还有大聪明会写成这样这个的意思就是说如果你确实给我了这个allowman这个值那我觉得你肯定就是把它写成false了因为默认就是数啊如果你用默认值的话为什么要给我呢这个显然也是错的对吧他可能比上一个错误好一丁点因为好歹他表达了一个python typing system可以表达的意思但是表达出来的这个意思不是我们真实的这个函数的实现原因其实和之前一样我们完全有可能是因为一个rapper就把之前那个捞难的值直接传进来了嘛或者哪怕我们不去思考rapper的事儿我就不能传一个和默认值一样的值吗这显然不对对吧所以这个overload也是不正确的那有的朋友问说好那请问我怎么表达这个观点呢我怎么来说明如果allow none是false的时候它一定会return int如果Aloud is true的时候就什么都有可能这个事我们前面其实提到了啊就没有办法了Python Typing System不支持这种表达方式事实上 Static Analysis 本来就不可能在依赖 runtime value 的时候来完成就是我这个值只有跑的时候才知道是多少我怎么可能在静态分析的时候给你分析明白呢这件事是不科学的对吧那你说我就要我就要我就要实际上这块确实有一个解我们这里可以把allow none变成一个literal也就是说这个argument只接受显示的true或者false就它不能是一个计算的结果它必须是明面写着的 true 或者 false然后当它是false的时候我们返回int我们在补充上当它是true的时候可能返回int或者真的对吧这个时候我们看 我们拿到age的时候如果把allow none直接写成true这个age+1会报错因为这个age有可能是Nan但是如果allowNan是false的话它就不会报错因为我们可以确定age是int那这个方案本身其实也不是歪门邪道这个方案在很多的时候都是有价值的就你确实可以通过某一个argument的值来决定这个函数的type但是这个值必须是在我们静态分析的时候就能分析出来的它不可以是一个通过计算得到的值那实际上在这里我们其实讲了另外一个知识点 对吧就是在typing system里面这个literal true false 和不到底有什么区别那如果未来大家对这个Python的Typing System感兴趣的话我会多介绍一些这个Python Typing System有关的知识好 那这一段时间就关注 这段时间关注 这里见 下来哦!