高天重构neosca计数模块 纠正频率相加数学硬伤与exec滥用

【Code Review】这么长一串相似代码怎么处理?另外数学不对也是错误哦! — 码农高天

码农高天

统计计数器合并时必须按累计分子除以分母而非直接累加频率,且业务代码应将静态配置抽离为字典驱动以彻底杜绝exec动态赋值。

AI-assisted summary of 【Code Review】这么长一串相似代码怎么处理?另外数学不对也是错误哦! — original source. Check the original for full context.

Source published:

Key ideas

  • Code Review 强调数据与逻辑分离原则:将构造函数中大段重复初始化的静态配置提取为独立数据文件,用外部配置驱动两行循环完成实例化,可大幅提高代码可维护性。 (0:00)
  • 避免使用类似 `self.w`、`self.s` 的散乱对象属性,建议用统一的字典结构(如 `self.structures`)集中管理实例,防止污染类命名空间并简化键名映射。 (0:00)
  • 审查指出加法运算符重载(`__add__`)中存在严重的数学逻辑错误:合并两个计数器时不能直接相加频率,因为分子与分母同时变化,衍生比率指标必须重新计算。 (5:00)
  • 严禁在合并属性或常规业务赋值中滥用 `exec()` 动态执行代码;动态属性访问应优先使用 `getattr`/`setattr`,重构为字典后则直接使用标准字典操作。 (5:00)

Detailed notes

## Background 本期视频由博主“码农高天”于2024年2月13日发布在YouTube平台。视频属于其Code Review系列,主要审查开源句法复杂度分析库neosca的核心数据结构模块,分享如何重构大段相似代码并纠正代码中的逻辑与数学计算错误。

## TL;DR 高天深入审查了neosca项目中负责句法结构计数的核心文件,指出其在初始化时硬编码大量静态常量导致的冗长与维护难题,建议通过“数据与逻辑分离”将配置抽离并用字典统一管理。随后,他针对代码中变量类型中途变更、元数据与统计数据混存等设计细节提出了优化建议。最后,高天指出了加法运算符重载中动用exec()动态赋值的反模式,并纠正了直接将两组频率简单相加的严重数学逻辑错误。

## Atmosphere & style 视频整体呈现出专业、坦诚且条理分明的技术评审氛围。高天在肯定作者良好排版习惯的同时直切要害,语气既有资深工程师的严谨犀利,又不乏循循善诱的启发感;当指出用exec()拼接代码和频率直接相加的离谱错误时,其直截了当的吐槽既接地气又具戏剧性,让观众在观摩代码重构的同时深切体会到规范工程实践与严谨数学逻辑的重要性。

## Bulletin - [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) Code Review 强调数据与逻辑分离原则:将构造函数中大段重复初始化的静态配置提取为独立数据文件,用外部配置驱动两行循环完成实例化,可大幅提高代码可维护性。 - [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) 避免使用类似 `self.w`、`self.s` 的散乱对象属性,建议用统一的字典结构(如 `self.structures`)集中管理实例,防止污染类命名空间并简化键名映射。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 审查指出加法运算符重载(`__add__`)中存在严重的数学逻辑错误:合并两个计数器时不能直接相加频率,因为分子与分母同时变化,衍生比率指标必须重新计算。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 严禁在合并属性或常规业务赋值中滥用 `exec()` 动态执行代码;动态属性访问应优先使用 `getattr`/`setattr`,重构为字典后则直接使用标准字典操作。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) 使用 `None` 作为函数的默认参数并在函数体内判定后再实例化 `set`,是避免 Python 可变对象默认参数共享陷阱的标准做法。 - [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) 类初始化时充斥大段建立静态实例的重复代码,应将配置数据剥离到独立模块,以实现数据与业务逻辑的解耦。 - [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) 使用形如 `self.w`、`self.s` 的离散属性如同用 `A_0`、`A_1` 代替数组,既污染对象命名空间又损害代码语义,应改用单一字典集中管理。 - [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) 在方法签名中添加裸星号(`*`)强制特定参数为关键字实参(Keyword-only arguments),可有效防止调用时传参顺序错位。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 保持局部变量生命周期内的类型一致性,避免在同一变量中从 `tuple` 动态转换为 `list`。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 在集合筛选和解析场景中,采用内置的 `filter` 函数相比手写循环在表达语义上更加清晰直接。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 若底层采用字典按键管理结构,导出字段时可直接支持包含斜杠等字符的键名,无需在后续处理中做字符串替换。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) API 返回的数据结构应进行清晰的分层设计,将文件名等元数据(`meta`)与统计频次(`freqs`)分开,避免同级平铺混存。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 在对象相加时直接对频率求和存在数学错误,合并衍生比率指标必须遵循分子累加除以分母累加的计算逻辑。 - [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 普通业务代码中应当坚决避免使用 `exec()` 这类黑魔法,可用内置反射函数或改用容器字典优雅解决属性合并问题。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) 代码结构与数据逻辑分离重构

在这一期 Code Review 中,码农高天审查了项目 `neosca`(一个用于句法复杂度分析的工具库)。尽管该项目的 README 制作精美附有动图,但 GitHub Actions 的构建处于失败状态。由于整个仓库体积较大,本期只专注于其中的核心数据结构文件 `structureCounter`。该模块包含两个核心类:`Structure` 用于保存单个句法结构的数据(如结构名称、描述、匹配模式 `pattern` 以及依赖的外部句法结构 `requirements`);`StructureCounter` 则是核心计数器,负责在接收输入文件后统计单词、句子、动词等多种句法结构的出现情况,并提供 `get_freqs` 等方法来导出词频。

在进入构造函数 `__init__` 后,输入参数包含 `selected_measures`(用于指定要统计的数据类型)。作者在此处的处理非常规范:当传入参数为 `None` 时新创建一个 `set`,避免了使用可变对象作为默认参数的常见陷阱。然而在接下来的初始化过程中,代码中充斥着大段大段建立 `Structure` 实例的重复逻辑。原作者虽然在排版上做得很工整,运用了合理换行和长字符串折叠技巧,但代码依然显得冗长杂乱。每一个 `Structure` 对象的属性名直接映射为结构的名称,同时绑定了若干固定的配置属性,这些本质上全部是静态常量。高天指出,这种场景最适合进行“数据与逻辑分离”: > “你要改数据就去找数据,要改逻辑就来找逻辑,会让代码可维护性变高一些。”

为了从根本上精简代码并提升扩展性,高天建议将所有构建 `Structure` 所需的常量属性抽取至独立的文件(例如配置数据模块),在 `__init__` 中仅通过两行代码完成循环导入与实例化。这样一来,日后如果要调整类名或重构逻辑,只需修改一处。此外,原代码将每一个结构实例挂载在 `self.w`、`self.s` 等独立的类属性上,这种做法等同于用 `A_0 = 0`、`A_1 = 1` 来代替数组,既污染了对象的命名空间,语义也不够明确。更好的做法是将所有结构集中维护在一个单一的字典 `self.structures` 中,直接使用结构名称作为键。对于构造参数中出现的关键字参数 `requirements`,为了避免调用者传参错位到 `pattern` 上,可以通过在方法签名中添加裸星号(`*`)来强制其必须以关键字形式传递(Keyword-only argument)。

```mermaid flowchart TD accTitle: 数据与逻辑分离重构流程 accDescr: 展示将硬编码类属性转换为外部常量数据并用字典驱动初始化的重构对比

subgraph Before["重构前:逻辑与数据耦合"] A1["__init__ 内部硬编码大量常量"] --> B1["动态绑定单个属性:self.w, self.s..."] B1 --> C1["代码冗长且命名空间被污染"] end

subgraph After["重构后:数据驱动初始化"] A2["独立配置文件 / 常量字典"] -->|import 引入| B2["循环遍历配置集合"] B2 --> C2["统一存入字典:self.structures[name]"] end ```

- 原仓库通过 `Structure` 对象保存元信息:结构名、描述、匹配模式(`pattern`)及前置依赖结构(`requirements`)。 - 默认参数陷阱防范:使用 `None` 作为默认值,在函数体内部判定为 `None` 时再实例化空 `set`,避免了共享可变默认实参。 - 命名空间与语义优化:避免使用 `self.w`、`self.s` 这类散乱属性,改用字典结构 `self.structures = {}` 统一按名索引。 - 强制关键字传参(Keyword-only arguments):在形参列表插入 `*`,确保 `requirements` 等重要配置项必须通过明确的 key 传入,防止位置参数错位。

---

[05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 类型一致性、字段转换与加法运算符重载中的数学错误

在完成基础结构的建立后,代码定义了候选集合变量 `structures_to_query`。原作者起初将其初始化为一个 `tuple`,随后为了配合后续的过滤操作又将其转化为 `list`,导致该变量在运行期间经历类型变化(标注为 `Sequence[Structure]`)。高天建议在能避免的情况下尽量不要让同一个变量在生命周期内存放多种类型,直接自始至终使用 `list` 即可,这本身也是一种微型的数据逻辑分离。同样的问题也出现在 `structures_to_report` 上。紧接着在解析用户传入的筛选度量时(`parse_selected_measures`),原代码手写循环对候选集合进行筛选,实际上使用内置的 `filter` 函数在语义上会更加清晰直接。

在导出字段与运算方面,代码暴露出若干设计上的别扭与潜在缺陷: 1. **字段名替换与字典格式**:原代码处理导出的字段时通过字符串替换将下划线(`_`)强行转换为斜杠(`/`)。如果此前按照建议使用字典管理结构,键名本身就可以直接包含斜杠,省去繁琐的字符串变换。 2. **元数据与指标数据混存**:在返回词频字典的方法中,代码将普通元数据(如 `file_name`)与句法结构计数结果直接混用同级 key 存放在同一个字典内。更规范的做法是对字典做层级拆分,例如外层分为 `meta`(存放文件名等元数据)和独立的 `freqs` 或 `structures` 字段,以提升数据接口的整洁度。 3. **加法操作符(`__add__`)中的数学逻辑错误**:类中重载了相除(`__truediv__`)和相加(`__add__`)操作。在合并两个 `StructureCounter` 实例时,代码直接将计算出的频率值相加。高天对此提出了尖锐的批评: > “频率的计算是分子除以分母的关系,当你把两个 counter 合并到一起的时候,是不是分子变了,分母也变了?那么新的频率怎么可能是两个原有频率的相加呢?所以我感觉在数学上这块就错了。” 4. **反模式代码滥用**:在加法合并属性时,原实现居然动用了 `exec()` 动态执行代码。高天强调,`exec()` 属于能避免就必须避免的黑魔法;即便不改用字典,也完全可以用内置的 `setattr()` 与 `getattr()` 替代;而一旦将底层重构成字典驱动,整个合并流程只需普通字典更新即可优雅解决。

- 变量类型稳定性:避免让变量在同一函数内从 `tuple` 动态变成 `list`,保持单一变量类型的纯粹性。 - 数据结构分层:API 返回的结果字典应分离业务元数据(如文件名)与统计指标(各结构频次),避免平铺导致 key 污染。 - 数学运算的严谨性:加权比率、发生频率等由“分子/分母”构成的衍生指标,合并统计时绝不可直接求和,必须重新用分子累加值除以分母累加值。 - 动态属性操作的安全性:严禁在简单业务赋值中使用 `exec()`,属性反射应使用 `getattr`/`setattr`,容器化重构则直接采用字典操作。

## Notable quotes > [00:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=0s) 你要改数据就去找数据,要改逻辑就来找逻辑,会让代码可维护性变高一些。 — 码农高天

> [05:00](https://www.youtube.com/watch?v=TKVJONxA_OM&t=300s) 频率的计算是分子除以分母的关系,当你把两个 counter 合并到一起的时候,是不是分子变了,分母也变了?那么新的频率怎么可能是两个原有频率的相加呢? — 码农高天

## Mentioned 码农高天, neosca, GitHub Actions, structureCounter, Python

## Video description 其他Code Review:https://www.youtube.com/playlist?list=PLSo-C2L8kdSNr5yUJYhyDArnM4FU9iG1S

Code Review是提升代码水平最快的方式之一,这期视频的主角是处理句法结构数据的data structure。

本次repo:https://github.com/tanloong/neosca/

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

中文简报

高天重构neosca计数模块 纠正频率相加数学硬伤与exec滥用 统计计数器合并时必须按累计分子除以分母而非直接累加频率,且业务代码应将静态配置抽离为字典驱动以彻底杜绝exec动态赋值。 高天深入审查了neosca项目中负责句法结构计数的核心文件,指出其在初始化时硬编码大量静态常量导致的冗长与维护难题,建议通过“数据与逻辑分离”将配置抽离并用字典统一管理。随后,他针对代码中变量类型中途变更、元数据与统计数据混存等设计细节提出了优化建议。最后,高天指出了加法运算符重载中动用exec()动态赋值的反模式,并纠正了直接将两组频率简单相加的严重数学逻辑错误。 Code Review 强调数据与逻辑分离原则:将构造函数中大段重复初始化的静态配置提取为独立数据文件,用外部配置驱动两行循环完成实例化,可大幅提高代码可维护性。 (0:00) 避免使用类似 `self.w`、`self.s` 的散乱对象属性,建议用统一的字典结构(如 `self.structures`)集中管理实例,防止污染类命名空间并简化键名映射。 (0:00) 审查指出加法运算符重载(`__add__`)中存在严重的数学逻辑错误:合并两个计数器时不能直接相加频率,因为分子与分母同时变化,衍生比率指标必须重新计算。 (5:00) 严禁在合并属性或常规业务赋值中滥用 `exec()` 动态执行代码;动态属性访问应优先使用 `getattr`/`setattr`,重构为字典后则直接使用标准字典操作。 (5:00)

Reading summary

码农高天在对句法复杂度分析库 neosca 的核心文件进行 Code Review 时指出,代码存在将静态数据硬编码在业务逻辑中、命名空间污染以及滥用 exec 的坏味道,同时指出其在合并计数器时直接累加频率值存在底层数学逻辑错误。 数据与逻辑分离及字典容器化 (0:00) 在原项目 core 模块的 `StructureCounter.__init__` 中,作者虽然合理运用了 `None` 判定避免默认可变实参陷阱,但充斥着大段排版整齐却冗长的 `Structure` 实例化代码。每一个实例的结构名、描述、匹配模式 `pattern` 与前置依赖 `requirements` 本质上都是静态常量。高天建议将这些配置属性完全抽取到外部模块,在构造函数中通过循环遍历批量构建,做到“改数据找数据,改逻辑找逻辑”。 此外,原作者将每个结构分别挂载到 `self.w`、`self.s` 等独立类属性上,这种做法相当于用 `A_0`、`A_1` 替代数组,既污染了对象的命名空间,语义也极不明确。高天指出更合理的做法是统一维护一个单一的字典 `self.structures` 并以结构名作为键;为了防止参数传递错位,还可以在构造函数的 `requirements` 参数前加入裸星号 `*`,强制要求其以关键字实参(Keyword-only arguments)形式传入。 变量类型纯粹性与数据分层设计 (5:00) 在候选集合处理中,原代码将 `structures_to_query` 和 `structures_to_report` 先初始化为 `tuple`,随后为了过滤又转换为 `list`,导致变量类型在运行期频繁变动。高天建议避免单一变量存放多种类型,自始至终采用 `list` 并使用内置 `filter` 函数处理筛选逻辑,以提升语义直接性。 在输出结构的设计上,原实现暴露出若干繁杂问题:由于前期未采用字典管理,导致后期需要通过字符串替换将字段名中的下划线强行转为斜杠;而在导出词频字典时,代码将诸如 `file_name` 等元数据与实际的句法计数指标平铺混杂在同级字典中。高天建议采用分层设计,将外层明确划分为 `meta` 和 `freqs` 等独立字段,保持数据接口的整洁清晰。 重载加法中的数学错误与 exec 滥用 (5:00) 在实现 `StructureCounter` 的加法操作符重载(`__add__`)以合并两份统计时,代码直接将两者的频率统计值相加。高天指出这在数学上完全错误:频率本质上是分子除以分母的比例关系,当两个计数器合并时,总样本与发生次数同时改变,新频率必须由合并后的分子除以合并后的分母重新计算,绝不能简单相加。 在实现该合并逻辑的过程中,原代码甚至使用了 `exec()` 来动态执行属性合并代码。高天强调 `exec()` 属于必须避免的反模式“黑魔法”;若未进行字典重构,也应使用内置的 `getattr()` 和 `setattr()` 进行属性反射,而一旦将底层改造为字典驱动,整个对象合并操作只需普通字典更新即可安全解决。 该 Review 表明,整洁的代码不仅需要依靠排版,更依赖于将静态数据与执行逻辑彻底解耦;同时,在重载运算与封装 API 时,必须严格保证底层数学公式的推导正确,并杜绝使用 exec 等高风险动态特性。

Source quotes

[0:00] 码农高天: 你要改数据就去找数据,要改逻辑就来找逻辑,会让代码可维护性变高一些。 [5:00] 码农高天: 频率的计算是分子除以分母的关系,当你把两个 counter 合并到一起的时候,是不是分子变了,分母也变了?那么新的频率怎么可能是两个原有频率的相加呢?

Original transcript

[0:00] 好 我们再带来一期code review的视频啊 这期视频呢 review的是neu sca 是一个这个语法复杂度分析的工具大家可以看到啊,Readme做的还是非常fancy的啊,有个gif在这儿虽然这个Build不知道为什么是Failing不过它这个酷比较大 所以我们今天只review其中的一部分我们今天来看的代码是structureCounter是一个用来统计各种巨发结构的数据的这么一个 data structure那我们照例是在网页版的VS Code中打开由于今天我们只看一个文件所以我们就不看左边的那个文件栏了我先来给大家简单介绍一下这个文件的结构这个文件有两个class这个class structure是保存一个锯法结构的数据的包括这种结构的名字描述它符合的pattern还有它的requirements需要什么其他的句法结构接下来这个structure counter是核心这里面就是存了很多个 structure对于一个输入文件去统计不同的句法结构比如说单词句子或者什么动词这样的东西的数据他大概的用法是这样的就是我有一个文件然后我告诉他我要统计什么样句法结构的信息比如说要统计单词和句子分别有多少然后可以通过getflex方法拿到这个词频之类的东西这个就是简单介绍一下这个class到底怎么用啊好 我们来看他的程序啊输入的selective measures就是统计哪些类型的数据这里我之前有个视频讲过对吧这里可千万不能给个默认值set可变对象是不能当默认值传进来的这个没有印下了回去放我的视频啊他这里的处理是正确的如果传进来一个NUN新开一个set否则用输入值接下来他就开始大量的建立structure我们可以往下看一眼啊可以看到啊他这个init函数里有一大部分都是在建立这个structure那这么写可以不可以呢当然是可以的像他这里也用了一些手法把比较长的东西尽量拆开像这种类型的换行包括一个长字符串怎么换行其实做的是不错的但是我们想一下即便它的代码风格其实做的挺好的我们看这一串的时候是不是觉得还是有点长而且我们实际上能感受到建立每一个structure其实是一套东西他对象的属性名就是structure的name然后每一个structure有若干个属性那这里如果是我我可能就会选择把数据和逻辑分开数据逻辑分离是我们之前聊到过很多次的话题我会把所有建立这些structure需要的属性都放到另外一个文件里面因为他们实在是太多了把它们放到逻辑里相对来说有点影响阅读同时我们要意识到这些属性是常量对吧包括下面需要的requirements也是长量所以建立每一个structure的时候所需要的输入其实是完全不变的它和这个init的输入是没有关系的所以我可能会在其他的文件里做这么一个大的数据把我需要用到的所有数据都放到这个东西里然后把他引破进来然后刚才整个的那一坨逻辑就变成了两行这样不光代码量减少了它还分隔了你的逻辑和数据当你需要修改逻辑的时候比如说未来我想把structure变成另外一个词假如说叫my structure那我只需要改一个地方就可以了这就是数据和逻辑分离的好处你要改数据就去找数据要改逻辑就来找逻辑会让代码可为忽性变高一些当然其实这里我们细看他使用的是self.w,self.s这些attribute就是属性对吧实际上这里完全没有必要使用 attribute当你每一个数据的意义其实非常相近的时候是不应该把这些数据存到不同的属性名上的这就相当于你明明要建立一个数族但是你一定要写成A下滑线0等于0A下滑线1等于1对吧它有一点污染命名空间,在雨意上也没有那么清晰所以这里我感觉写成这样会更好一点你把所有的structure都放到这个self-structures里然后直接用这个structure的名字作为key就完事了这个是我对这一长串的观点啊好我们接着往下看我们看后面这里出现了一个keyword argument requirements那这里他之所以需要使用 keyword argument是因为他直接写会写到pattern这个位置这里其实是调用的时候自己容易出的错误就我以为我在写requirements实际上我写的不是requirements那如果我希望这个requirements永远是通过keyword的方式传进来的 [5:00] 可以加个星号啊星号之后的argument代表必须要通过keyword的方式传进来当然这里可能把这个星号放到meshes前面更合适啊好接着往下看在建立了一堆structure之后他这里开了一个structure to query这个变量呢我刚开始没弄懂它为什么要做一个sequence structure他明明建立的是一个tuple对吧他为什么要把它变成sequence读到后面的时候我就发现了原来他要把这个变量啊再filter一遍变成list所以这个变量呢就有数是tuple 有数是list 他就把它变成了一个sequence实际上像这种情况 你完全没有必要建立一个tuple你直接开一个list就完了呀这样就可以保证这个变量永远是list了就这个tubo本身建立的价值并不大一般情况下在能避免的情况下我推荐大家尽量不要把两种以上类型的数据存到一个变量里那当然这里他可以改写一下对吧他就可以写成这样对吧也是一个小的逻辑数据分离后面这个structures to report是一个道理啊我们就不说了我们可以看到建立完这两个candidate之后呢他做了一个parse selected measures大家还记得吧这个Selectic Matters是用户传进来的他想要的数据然后在这就能看得比较清楚了它本质上就是把刚才的那些数据给filter了一下当然提到了filter对吧这里就可以直接用语义上更明确的filter可以写成这样对吧当然他现在写的没有任何问题啊两个都是可以的然后呢他给了一个field 应该是用逗号隔开的各种field这块稍显奇怪但也不算离谱最奇怪的是他把下滑线变成斜杠这有可能是人家的标准格式就是使用的斜杠但是如果你刚才使用的是字典而不是属性的话你的key本身就可以带斜杠而不是必须用下发线了那这里是不是就同意了接下来的两函数就是数学运算比较直接没什么可说的大家这里注意一下他overload了true div然后两个structure object才能进行相除这里返回磁频的部分其实稍微有点别扭这里和前面的field name是一致的他把这个file name和这个句法结构都当做同样的key放到了一个字典里这离我多少觉得是有一点别扭啊如果是我的话 我可能会把这个字典单独再分一层比如说你有meta data可能是file name对吧然后你再分一层比如叫Fregs或者叫Structures然后里面再放数据我感觉可能更合理啊最后他实现了一个两个structure counter的加法这里前面的文件名,Selective Measures都没什么问题但是这个部分是有问题的首先我们不说代码频率是可以直接加起来的吗我们知道频率的计算是分子除以分母的关系当你把两个counter合并到一起的时候是不是分子变了 分母也变了那么新的频率怎么可能是两个原有频率的相加呢所以我感觉在数学上这块就错了另外exec这个函数我们是能避免就避免对不对这里显然可以用setattr跟gett来解决问题但是如果像我刚才说的那样把它们都变成字典了这里是不是就更好写了是不是发现在这一路上 字典其实都给我们带来了更多的便利这行代码是稍稍有点走偏了不建议大家这么写好那这就是我们今天这期的code review了希望大家有所帮助如果大家喜欢的话未来我们还会做更多的code review喜欢的话记得点下关注别忘了一键三连哦优优独播剧场——YoYo Television Series Exclusive