【python】python中什么会被当真?你知道if判断背后的规则吗?

【python】python中什么会被当真?你知道if判断背后的规则吗? — 码农高天

码农高天

AI-assisted summary of 【python】python中什么会被当真?你知道if判断背后的规则吗? — original source. Check the original for full context.

Source published:

Key ideas

  • CPython 条件跳转字节码指令通过快速通道处理 True 与 False 单例,其他所有对象的布尔求值统一委托给底层核心函数 PyObject_IsTrue。 (0:00)
  • Python 自定义类的真假判断严格遵循双层查找:优先调用 __bool__,若未定义则回退至 __len__;若两者均未定义,默认当作 True 处理。 (5:00)
  • CPython 通过在底层强制校验 __bool__ 必须返回严格的 bool 类型、__len__ 必须返回合法长度数值,从机制上杜绝了递归求值死循环。 (5:00)
  • 内置的 bool 本质是一个类(PyBool_Type)而非普通函数,在 if 语句中显式书写 bool(a) 与 if a: 在判断逻辑上完全等价且会带来冗余调用开销。 (10:00)

Detailed notes

## Background 该视频由开发者“码农高天”于 2024 年 1 月 25 日发布在 YouTube 平台上。视频简介指出,本期内容围绕 Python 中 `if` 条件判断背后的底层规则展开,探讨在 Python 这一灵活的语言中,任意类型的变量是如何被当作布尔值进行真假判定的。

## TL;DR 视频深度解析了 Python 中四种条件判断写法(`if a is True:`、`if a == True:`、`if a:` 与 `if bool(a):`)的机制差异与适用场景。通过翻阅 CPython 源码,主讲人展示了字节码指令 `POP_JUMP_IF_FALSE` 及核心求值函数 `PyObject_IsTrue` 如何对单例、数值与容器类型进行真假判断。针对自定义对象,视频拆解了优先调用 `__bool__`、次选 `__len__` 以及无定义时默认回退为真的协议与类型防御细节。最后指出 `bool` 本质是类而非普通函数,证明了在 `if` 语句中套用 `bool(a)` 是纯粹的冗余开销,并呼吁在工程中通过明确约束类型来提升代码健壮性。

## Atmosphere & style 整体氛围硬核严谨且条理清晰,兼具技术博主的从容与底层探究的求真感。主讲人直接带着观众逐行研读 CPython 源码与字节码,以抽丝剥茧的方式打破诸如“`bool` 是函数”或“自定义类走底部分支”等常见直觉误区。解说语调平实自信,在提供底层细节的同时频繁结合日常业务代码规范给出建议,给观众带来一种既通透又极具工程指导意义的学习体验。

## Bulletin - [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) CPython 条件跳转字节码指令通过快速通道处理 True 与 False 单例,其他所有对象的布尔求值统一委托给底层核心函数 PyObject_IsTrue。 - [05:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=300s) Python 自定义类的真假判断严格遵循双层查找:优先调用 __bool__,若未定义则回退至 __len__;若两者均未定义,默认当作 True 处理。 - [05:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=300s) CPython 通过在底层强制校验 __bool__ 必须返回严格的 bool 类型、__len__ 必须返回合法长度数值,从机制上杜绝了递归求值死循环。 - [10:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=600s) 内置的 bool 本质是一个类(PyBool_Type)而非普通函数,在 if 语句中显式书写 bool(a) 与 if a: 在判断逻辑上完全等价且会带来冗余调用开销。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) 日常开发中常见的四种条件判断写法语义截然不同:if a is True 判断对象本体,if a == True 容易因操作符重载引发异常逻辑,而 if a: 与 if bool(a): 则是隐式与显式布尔求值。 - [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) 使用 if a is True 时其反面绝不仅是 a is False,在编写分支代码时必须警惕变量可能是除两个布尔单例之外的任意对象。 - [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) 在 if a: 生成的 POP_JUMP_IF_FALSE 字节码中,若操作数非 True/False,均会调用返回值为整型状态码(1 为真、0 为假、-1 为错误)的 PyObject_IsTrue。 - [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) PyObject_IsTrue 首先匹配单例:True 立即返回 1,False 和 None 则直接返回 0,这解释了为什么 None 在条件语句中总被视作假。 - [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) 数值类型的真假由其 tp_as_number 槽位中的 nb_bool 决定,判定规则为非零即真、等于零即假。 - [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) 容器类型的真值判断取决于其长度:dict 和 set 走 mapping 槽位,list、str 和 tuple 走 sequence 槽位,底层均以长度是否大于 0 来裁定。 - [05:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=300s) 用 class 语法定义的 Python 自定义类具有非空的槽位指针,其真值裁定首先查找 __bool__,缺省时查找 __len__,二者皆无时默认返回真。 - [05:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=300s) 为了规避底层无限递归,CPython 在第 6585 行显式通过 PyBool_Check 检查 __bool__ 的返回值,若不是严格的布尔类型会直接抛出 TypeError。 - [05:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=300s) 在工程实践中应尽量压缩变量的类型范围,避免在变量可能是 None、0 或空列表的混合场景中滥用 if a:,因为空容器与缺失值往往需要不同的业务处理分支。 - [10:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=600s) bool(a) 在 CPython 内部调用 PyBool_Type 的构造函数 tp_new,其内部同样是通过 PyObject_IsTrue 计算真伪后再由 PyBool_FromLong 构造布尔单例。 - [10:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=600s) 在 if 判断中使用 if bool(a): 相比 if a: 没有任何语义或真值判定的改变,纯粹平添了一层函数/类型调用的开销。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) 四种 if 条件判断的差异与 `if a:` 的字节码机制

在 Python 编程中,当我们想判断一个变量 `a` 时,常常会看到四种写法:`if a is True:`、`if a == True:`、`if a:` 以及 `if bool(a):`。如果把变量 `a` 赋值为 `True`,这四个语句都会成立并打印出四个 `True`;如果 `a` 赋值为 `False`,这四个语句则全都不成立,什么都不会打印。然而,一旦变量 `a` 是 `True` 和 `False` 之外的其他值,这四种写法的行为和底层逻辑就大相径庭。

```mermaid flowchart TD accTitle: Python 条件判断流程与分支路径 accDescr: 展示了从字节码到 PyObject_IsTrue 以及针对单例、内置类型与自定义对象的真值判定流程。

subgraph FastPath ["快速通道 (Fast Path)"] A["POP_JUMP_IF_FALSE 字节码"] --> B{"条件是否为单例?"} B -- "是 True / False" --> C["跳过函数调用直接跳转"] B -- "其他对象" --> D["调用 PyObject_IsTrue(v)"] end

subgraph BuiltinTypes ["内置类型分支"] D --> E{"是否为 True / False / None?"} E -- "True 返回 1" --> RET_TRUE["返回 1 (True)"] E -- "False / None 返回 0" --> RET_FALSE["返回 0 (False)"] E -- "否" --> F{"数值类型 (tp_as_number)?"} F -- "是" --> G["nb_bool(): 是否非零?"] G -- "非零" --> RET_TRUE G -- "等于 0" --> RET_FALSE F -- "否" --> H{"映射或序列类型?"} H -- "Mapping / Sequence" --> I["检查 length 是否大于 0"] I -- "> 0" --> RET_TRUE I -- "= 0" --> RET_FALSE end

subgraph CustomTypes ["Python 自定义类分支"] H -- "自定义类 (slot 非空)" --> J{"定义了 __bool__?"} J -- "是" --> K["调用 __bool__() (必须返回 bool)"] J -- "否" --> L{"定义了 __len__?"} L -- "是" --> M["调用 __len__() (必须返回有效长度)"] L -- "否" --> N["默认返回 1 (当成 True)"] K --> O["PyObject_IsTrue 检查返回值"] M --> O O --> P{"判断结果"} P -- "真 / 长度 > 0" --> RET_TRUE P -- "假 / 长度 == 0" --> RET_FALSE end ```

第一种写法 `if a is True:`,判断的是 `a` 是不是 `True` 本身。如果 `a` 是 `True` 之外的任何对象,哪怕是在布尔上下文中为真的对象,条件也绝不会通过。它适用于在多种数据种类中唯独想把 `True` 拿出来单独作为特殊 case 的场景。但码农高天强调,写代码时必须清楚“`a is True` 的反面不是 `a is False`”,因此在使用 `else` 时必须清醒地意识到: > “在我们写程序的时候,脑子里面一定要想着,这个变量它有可能是 True 和 False 之外的东西。”

对于第二种写法 `if a == True:`,高天直言“这个用法就基本上不应该出现了”。在 Python 中 `==` 运算符可以被重载,如果开发者真的想确认它是否为 `True` 本人,应该使用 `is`;使用 `==` 去判断一个自定义对象是否等于 `True`,其逻辑在绝大多数工程场景下都显得十分诡异且不合理。

第三种写法 `if a:` 是日常开发中的核心。通过查看 Python 字节码,会发现它的字节码非常简洁,核心指令为 `POP_JUMP_IF_FALSE`。翻看 CPython 源代码实现,该指令前有两个快速通道(fast path):若条件本身直接就是 `True` 或 `False`,便能省去一次函数调用直接跳转。如果不是这两个布尔单例,代码在第 3187 行调用核心函数 `PyObject_IsTrue`: > “实际上在 Python 内部进行 condition 计算的时候,它都是使用的这个 `PyObject_IsTrue` 函数。”

- `PyObject_IsTrue` 的返回值类型为 `int`,其中返回 `1` 代表 `True`,返回 `0` 代表 `False`,返回 `-1` 代表执行过程中发生错误。 - 函数首先检查最常见的内置单例:如果对象是 `True` 则返回 `1`;如果是 `False` 或者 `None` 则直接返回 `0`。这就从源码底层解释了为什么 `if None:` 会直接被当作 `False` 处理。 - 随后针对内置数值类型(如 `int`、`float`、`complex`、`decimal` 等),源码会查找其 `tp_as_number` 中的 `nb_bool` 函数。各种数值类型的规则是:如果等于 0 则返回 `False`,非零值则返回 `True`。 - 对于容器类型,源码分别检查 `tp_as_mapping` 与 `tp_as_sequence` 的长度函数。像键值对形式的 `dict` 以及基于哈希实现的 `set` 采用 mapping 的长度检测,而 `str`、`tuple`、`list` 等有序序列则采用 sequence 的长度检测。两者底层均通过判断容器长度(length)是否为 0 来决定:长度为 0 返回 `False`,非 0 则返回 `True`。

---

[05:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=300s) 自定义数据结构的真值协议与无限递归规避

许多人看到 CPython 源码中的 `if-else` 结构时,会直觉地以为自定义对象如果不是内置类型,就会直接掉入底部的 `else` 分支。然而高天指出事实并非如此:底部的 `else` 仅在对象的类型既没有 `tp_as_number`,也没有 `tp_as_mapping` 和 `tp_as_sequence` 时才会触发,这种情况只有在 C 语言扩展中自定义类型时才可能出现。对于在 Python 代码中用 `class` 语法定义的任何自定义类,它们的 `tp_as_number` 等结构指针并不是 `NULL`。

在 Python 层面定义的类会统一走入特定的槽位(slot)分配机制。类型上的 `nb_bool` 槽位调用了一个专门的处理函数,该函数严格按照以下顺序执行两件事: 1. **优先查找 `__bool__` 方法**:若类中定义了双下划线 `__bool__` 方法,则直接调用该方法。 2. **次选查找 `__len__` 方法**:若没有定义 `__bool__`,则退而求其次寻找是否定义了双下划线 `__len__` 方法。 3. **默认回退机制**:若既没有定义 `__bool__` 也没有定义 `__len__`,该函数直接返回 `1`。

> “也就是说如果你的自定义数据结构既没有定义这个 bool,也没有定义这个 length,那他在这个 if statement 里相当于是 if True。”

取得这些魔术方法的返回值后,CPython 内部会再次调用 `PyObject_IsTrue` 对其进行评估。此时可能会引发一个疑问:外层通过 `PyObject_IsTrue` 调入该函数,内层又再次调用 `PyObject_IsTrue`,是否存在死循环或无限递归(infinite recursion)的风险?

答案是绝对不会。CPython 源码在两处做了强类型校验以杜绝无限递归: - 如果调用的是 `__len__`,源码会强制检查返回值是否为合法的非负整数,若不是可以表示长度的类型会立即抛出异常;合法的长度数值在下一步便能直接判定为真或假。 - 如果调用的是 `__bool__`,CPython 源码在第 6585 行显式检查其返回值类型是否为精确的 `bool`(`PyBool_Check`),若不是布尔类型,在第 6589 行会直接抛出 `TypeError` 报错。

得到返回值后,底层代码做了一个规整化操作:若返回值大于 0 则直接返回 1;对于非正数则进行降采样类型转换(downcast),保留 0 或负数错误码。在日常工程编码中,高天强调: > “我其实还是更推荐大家去做更明确的判断,其中一个就是去保证 type,你可以把 A 这个值的 type 范围压缩的越小越好。”

因为 `if a:` 对于 `None`、`False`、`0` 以及空列表 `[]` 均判定为假,而在复杂的业务系统中,遇到空列表与遇到 `None` 往往需要完全不同的应对逻辑。因此只有在变量类型非常收敛、明确的情况下才适合简写为 `if a:`。

---

[10:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=600s) `bool` 的本质与 `bool(a)` 在条件判断中的冗余

关于第四种写法 `if bool(a):`,很多人直觉上认为 `bool` 是一个返回布尔值的普通内置函数。然而从 CPython 源码来看,`bool` 根本不是函数,而是一个内置的类(class)。在 `bltinmodule.c` 中,`bool` 对应的其实是 `PyBool_Type`。

查看 `boolobject.c` 中该类型的 `tp_new` 构造函数,在第 52 行可以看到,它在构造新的布尔实例时,底层调用的依然是老朋友 `PyObject_IsTrue`: - `tp_new` 调用 `PyObject_IsTrue` 计算入参的真假性。 - 随后直接调用 `PyBool_FromLong`,将结果包装并返回全局单例 `Py_True` 或 `Py_False`。

正因为 `bool(a)` 表面上像一个函数,但其实际构造过程完全依托于 `PyObject_IsTrue`,所以代码中的 `if bool(a):` 与单纯的 `if a:` 在真值判断逻辑上是完全等价的。

两者的唯一区别在于运行效率:在 `if` 语句中显式套上一层 `bool(a)`,只是额外增加了一次函数/类型的调用开销(function call),完全属于多此一举的操作。当然,在需要显式将一个任意数据显式转换为标准的 `True` 或 `False` 单例并进行传递、存储时,`bool(a)` 依然有其存在的价值;但若仅仅放在 `if` 关键字后作条件分支,则没有任何额外作用。最后,码农高天在视频末尾提醒观众如果喜欢内容记得点击关注,不要忘记“一键三连”。

## Notable quotes > [00:00](https://www.youtube.com/watch?v=QxOdRkXpQwE&t=0s) 在我们写程序的时候,脑子里面一定要想着,这个变量它有可能是 True 和 False 之外的东西。 — 码农高天

## Mentioned Python, CPython, 码农高天, PyObject_IsTrue, POP_JUMP_IF_FALSE, PyBool_Type, PyBool_Check, PyBool_FromLong, boolobject.c, bltinmodule.c

## Video description 这期视频聊一下if里判断的规则。在python这种灵活的语言里,一个任意类型的变量是怎么被当成bool值进行真假判断的呢?

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

中文简报

Python中什么会被当真?揭秘if判断背后的规则 视频深度解析了 Python 中四种条件判断写法(`if a is True:`、`if a == True:`、`if a:` 与 `if bool(a):`)的机制差异与适用场景。通过翻阅 CPython 源码,主讲人展示了字节码指令 `POP_JUMP_IF_FALSE` 及核心求值函数 `PyObject_IsTrue` 如何对单例、数值与容器类型进行真假判断。针对自定义对象,视频拆解了优先调用 `__bool__`、次选 `__len__` 以及无定义时默认回退为真的协议与类型防御细节。最后指出 `bool` 本质是类而非普通函数,证明了在 `if` 语句中套用 `bool(a)` 是纯粹的冗余开销,并呼吁在工程中通过明确约束类型来提升代码健壮性。 CPython 条件跳转字节码指令通过快速通道处理 True 与 False 单例,其他所有对象的布尔求值统一委托给底层核心函数 PyObject_IsTrue。 (0:00) Python 自定义类的真假判断严格遵循双层查找:优先调用 __bool__,若未定义则回退至 __len__;若两者均未定义,默认当作 True 处理。 (5:00) CPython 通过在底层强制校验 __bool__ 必须返回严格的 bool 类型、__len__ 必须返回合法长度数值,从机制上杜绝了递归求值死循环。 (5:00) 内置的 bool 本质是一个类(PyBool_Type)而非普通函数,在 if 语句中显式书写 bool(a) 与 if a: 在判断逻辑上完全等价且会带来冗余调用开销。 (10:00)

Reading summary

码农高天通过剖析 CPython 源码指出,Python 中条件判断的核心是底层的 `PyObject_IsTrue` 机制;在 `if` 语句中四种常见真值写法的底层逻辑与开销截然不同,开发者应警惕隐式类型转换带来的工程隐患。 四种 if 条件写法的语义差异与适用边界 (0:00) 在 Python 中,当变量为布尔单例时,`if a is True:`、`if a == True:`、`if a:` 以及 `if bool(a):` 表现一致,但面对其他对象时行为大相径庭。`if a is True:` 仅用于精确匹配 `True` 单例的特殊情况,但高天提醒“`a is True` 的反面并非 `a is False`”,使用 `else` 时须防范变量为其他类型。对于 `if a == True:`,由于 `==` 运算符可被重载且逻辑诡异,高天认为该写法在工程中基本不应出现。 日常最常用的 `if a:` 对应字节码指令 `POP_JUMP_IF_FALSE`。在进入判断前,该指令包含跳过函数调用的快速通道;若变量非布尔单例,底层则统一调用 `PyObject_IsTrue` 函数计算条件。 PyObject_IsTrue 内置类型与自定义对象的评估链 (0:00) `PyObject_IsTrue` 返回 `1`(真)、`0`(假)或 `-1`(异常)。对于单例,它将 `True` 评定为 1,`False` 与 `None` 评定为 0;对于数值类型,通过 `nb_bool` 判断是否非零;对于 `dict`、`set` 等映射与 `list`、`str` 等序列,则通过各自槽位检测长度是否大于 0。 对于通过 `class` 语法定义的自定义类,其槽位指针非空,会进入特定槽位分配机制,严格遵循回退链:优先调用 `__bool__`,若无则调用 `__len__`;若两者均未定义,则默认返回 1,即直接视作 `True`。 无限递归防范机制与工程类型收敛建议 (5:00) 针对内外部均调用 `PyObject_IsTrue` 是否会导致无限递归的疑问,源码通过强类型校验杜绝了这一隐患:若调用 `__len__`,系统强制要求返回非负整数否则报错;若调用 `__bool__`,源码显式使用 `PyBool_Check` 验证返回值必须为精确的 `bool` 类型,否则直接抛出 `TypeError`。 由于 `if a:` 会将 `None`、`False`、`0` 及空容器全部判定为假,但在业务逻辑中各假值的含义截然不同,高天建议开发者尽量压缩并收敛变量的类型范围,避免在类型模糊的情况下盲目简写为 `if a:`。 bool 的类型本质与 bool(a) 的调用开销 (10:00) 针对 `if bool(a):` 写法,CPython 源码显示 `bool` 并非普通内置函数,而是类 `PyBool_Type`。在其构造函数 `tp_new` 中,底层同样是调用 `PyObject_IsTrue` 计算真假,再由 `PyBool_FromLong` 包装返回全局单例 `Py_True` 或 `Py_False`。 因此,在 `if` 语句中使用 `bool(a)` 在逻辑上与 `if a:` 完全等价,不仅无法提供额外的校验价值,反而平添了一次函数与类型实例化的调用开销;显式类型转换仅在需要对外传递、存储布尔单例时才有必要保留。 Python 的条件判断体系依托高效的快速通道与多层槽位回退机制,但 `if a:` 抹平了空值、零值与布尔假的语义边界;编写严谨代码的关键在于明确变量类型约束,并在分支判断中消除多余的类型构造调用。

Source quotes

[0:00] 码农高天: 在我们写程序的时候,脑子里面一定要想着,这个变量它有可能是 True 和 False 之外的东西。

Original transcript

[0:00] 这期视频我们来给大家讲一讲在拍档里面什么会被当真当然这里的当真指的是当我们使用if语句来进行条件判断的时候什么条件会被认为是通过了我们看左边这边代码变量A被复制成了True然后我下面有四种if的判断 一个是if a is true 然后if a 等于等于true接下来是if a和if不那当然了 在a=true的时候 这四个语句都是成立的 它会打印出来四个true在A=fals的时候 这四个语句显示都不成立的他就什么都不会打印也就是说当我们要判断的变量是true或者false的时候这四种判断方式的结果是一致的那你知道这四种判断方式有什么不同吗我们先说第一个第一个判断呢 判断的是A是不是true如果A是true之外的任何一个东西 这个条件判断都不会通过那这种判断方式适用于你想在很多的数据种类中把true这个值单独拿出来做一个case的情况就是在这个条件下 我只要A是true的情况在这里当然要注意一下A is true的反面不是A is false所以A is not true在我们写程序的时候 脑子里面一定要想着 这个变量它有可能是true和false之外的东西Python是个很灵活的语言有很多正确的有争议的甚至不太正确的用法我们都是要注意的也就是说在使用这种用法的时候你要知道这个else是会成立的对所有true和false之外的值成立这是我们说的第一种写法它没有那么常见但是在有些情况下当你很明确的知道自己在做什么的时候会用到它第二种写法A等于等于处这个用法就基本上不应该出现了原来我们在之前的一期讨论Nan的视频里面其实说过就是等于等于这个运算符呢 在python里面是可以被重载的那如果你真的是要判断他是不是true本人你应该用上面的 is否则的话你会发现这个用法非常的奇怪 你比ins多的是判断一个customobject等于等于true这件事是不是成立就它的逻辑听起来不太合理当然我们今天的重头戏是在这个if a 冒号上就当我们写这个if a 冒号的时候python到底是怎么判断这个条件是不是成立的好 那我们老规矩先看字节盲字节码这里呢其实它非常简单它只有一句叫 pop jump if false打开CPATEN的原码找到这个BICCODE可以看到前面是有两个fast path也就是如果这个condition本身就是true或者是false的话它可以少一个function call否则的话注意3187行它调用的是这个py object is true 函数实际上在python内部进行conditioned计算的时候它都是使用的这个py object is true 函数好 我们看这个pyObjectIsTrue函数 它的返回值是一个int1代表true 0代表false这里他还写了有可能return-1代表出错了我们看前三个判断是最常见的如果他本身就是true的话 返回1如果就是false或者就是non的话 返回0这里出现了第一个很常见的情况如果if A 是None的话 会判断成false下面这个部分 找了一下这个NB部它是什么呢?对于内置类型来说就是作为一个树他的Boolean返回值那python有若干个内置的number结构比如说 integer floatcomplex, decimal那所有的表示输入的这些数据结构 如果它等于0 就会返回一个false如果非零就会返回一个true当然这是我们看到的这个behavior实际上的实现是在每一个不同类型的NBBull函数来完成的也就是对于所有python的数来说if a判断的就是a是否等于0接下来这两个部分分别是去看的TPS Mapping 跟TPS Sequence 的 length那对于内置数语结构来说Mapping就是这种key value pair的形式那最常见的呢就是dictionary包括Set也是用的Mapping的Lengthsequence就更多一些就是可属有序的序列像string,tuple,list这些都是一种sequence那么无论对mapping还是对sequence来说这个py object is true 都是判断的他们是不是空也就是他们的length [5:00] 是不是零如果是零的话返回false如果非零的话返回true这样大家就应该有个大致的了解了对于所有的内置数据结构来说如果它是一个数就判断它是不是零如果它是一个序列或者是一个mapping就判断它是不是空好 那这是对内置的数据结构如果我们判断的是一个自定义的数据结构呢可能很多人都觉得 这个容易就直接到这个else case了然而不是这样的啊这个else case是要在v这个type既不能s number 也不能s mapping 也不能s sequence 的时候 才会被运行的然而所有你在python中自定义的数据结构它的s number,s mapping,s sequence都不是闹只有你在C里面自定一个type才有可能会用到这个else case在python里面自定义的数据结构实际上是会落到这个case下因为这TPS number不是Now而且它有一个NBBull函数NBBOOT函数实际上是每个type上面预留的一个slot这个部分呢我们在未来会细讲你现在只需要知道它实际上是调用了我们面前的这个函数这个函数做了两件事第一他找你这个自定义的数据结构有没有定义下锅线下锅线不如果定义了用这个函数接下来如果你没有定义的话 他去找你是否定义了下滑线下滑线 length如果定义了 那么使用这个lens函数都没有定义的话 直接return1也就是说如果你的自定义数据结构既没有定义这个ball 也没有定义这个length那他在这个if statement里相当于是if true当然在拿到这个function之后他就使用这个function拿到一个value 然后再用这个py object is true去判断这个value是不是应当被当做true来考虑那这个地方你可能会想了这里不是有可能会出现一个recursion吗就有没有可能出现一个无限地归的情况因为我们进到这个函数来是py object is true进来的这个函数又调用了py object is true这里是不会的为什么呢因为这个下方线下方线的LENF函数当你返回这个值的时候他会去检查这个值如果你返回的不是一个可以表示Length的值他会直接给你报错的那你返回的这个合法值一定可以在一步之内变成一个true or false而夏文肖先生不返回的值他这里6585行先check了一下他的type是不是不如果不是的话看6589行他会报个错这样就避免了一个无限的地规那在最后的时候呢他的返回值做了一点点小处理如果你return值是大于0的就直接返回1因为我们上面说过有些返回值是它的长度他就不返回长度了否则的话他做了一个downcast这里相当于就是返回了一个 int res如果是零的话还是返回的是零然后我们刚才提到过这个NBBOOL的返回值呢可能是一个负数这样在错误的情况下可以返回一个复数好 那我先讲完这个之后呢 大家都知道这个if a到底意味着什么东西了你以后自己在使用if A的时候 你心里就有个底知道什么样的内置结构在这里会被当成true 什么样会被当成false如果你自己写的一个数据结构在这里的判断结果是什么样的那当然我其实还是更推荐大家去做更明确的判断其中一个就是去保证type你可以把A这个值的type范围压缩的越小越好就像我们之前那期视频讲过的if A这里none和false包括空的list他都会判断是false但是有时候你的程序面对这几种情况你可能想要的是不同的behavior所以这个if a当你非常确定A有可能是什么类型的时候你可以使用否则的话我会建议你做更多的检查最后我们说一下布偶A首先问一下大家布是个什么东西肯定有非常多人会回答它是一个函数对吧一个返回True of Fox 函数那实际上呢它是一个class是一个内置的数据结构我们看在built-in module C里面呢这个Bull实际上是PipeBull Type他是一个class那这个bow object呢它代码在这个bow object.c里面我们看一下它这个new函数第52行依然是调用了PyObjectIsTrue然后它的返回值直接是Pipe from Law也就是这个new函数它的返回值永远是true或者false [10:00] 看起来就像是一个直返回true或者false函数一样那由于他实际上也是用的py object is true这个函数因此第12行if bull跟前面的if a是等价的他们俩没什么区别那当然如果直接在 if statement里用呢这个布还多了一个方生铐所以完全没有必要这么用当然在有些情况下你确实想把一个数据给cast到一个boon值true或者false这个函数还是有用的只是直接跟在if statement里面作用不是非常大好 那今天的视频就到这里 喜欢的话记得点一下关注 别忘了一键三流哦!