码农高天借Pytest钩子与Docstring标签化解异构CI调度难题

用注释对测试进行分类?这给你能的! — 码农高天

码农高天

通过在Docstring中嵌入标签并结合Pytest收集钩子,开发团队无需重构用例即可建立Opt-in过滤机制,根治传统跳过装饰器易漏配挂死CI的问题。

AI-assisted summary of 用注释对测试进行分类?这给你能的! — original source. Check the original for full context.

Source published:

Key ideas

  • 针对 GitHub CI 虚拟机缺乏 GPU 的硬件限制,码农高天提出了一种基于文档字符串标签的轻量测试分类方案,有效解决了异构环境下的用例调度问题。 (0:00)
  • 传统基于目录分类或 unittest.skipIf 装饰器的方案因多维冲突、侵入性强以及容易漏标导致 CI 挂掉等痛点,在实际项目中难以落地。 (0:00)
  • 通过在 conftest.py 中利用 Pytest 的 pytest_addoption 与 pytest_collection_modifyitems 两个 Hook 钩子,可在不依赖第三方插件的情况下原地过滤包含 #cpu 等标签的测试用例。 (3:40)
  • 基于 Docstring 的 Opt-in 模式将代码修改成本降至最低,既不改变开发者的编写习惯,又天然保障了未标注测试在受限 CI 环境下的安全性。 (3:40)

Detailed notes

## Background 该视频由码农高天于 2024 年 12 月 13 日发布在 YouTube 平台。视频简介指出,在多环境与不同场合下对测试用例进行合理分类是常见需求,作者以此分享了自己在实际工作中遇到的硬件依赖冲突问题及最终落地的解决方案。

## TL;DR 面对 GitHub CI 仅有 CPU 资源而仓库中混杂 GPU 依赖测试的场景,传统的按目录分流、`skipIf` 装饰器或基类继承方案存在样板代码臃肿、高侵入性及 Filter-out 模式易挂 CI 等硬伤。高天提出了一种利用测试函数 Docstring 标注 `#cpu` 标签的轻量解法,兼顾了快速测试等多维度扩展。在框架底层,只需在 `conftest.py` 中利用 Pytest 的 `pytest_addoption` 与 `pytest_collection_modifyitems` 钩子函数,便能在不破坏开发者原有代码习惯的前提下,以极少代码实现安全优雅的 Opt-in 动态测试集过滤。

## Atmosphere & style 视频展现出务实而接地气的工程探讨氛围,风格明快利落且充满极客趣味。讲解从最直观解法的逐一碰壁展开,一步步将听众带入工程权衡的推导逻辑中,生动还原了一线开发者的思考脉络。整场分享像是一位资深技术专家在白板前与同事复盘架构设计,在四两拨千斤的“奇招”落地瞬间给人带来强烈的解题快感。

## Bulletin - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 针对 GitHub CI 虚拟机缺乏 GPU 的硬件限制,码农高天提出了一种基于文档字符串标签的轻量测试分类方案,有效解决了异构环境下的用例调度问题。 - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 传统基于目录分类或 unittest.skipIf 装饰器的方案因多维冲突、侵入性强以及容易漏标导致 CI 挂掉等痛点,在实际项目中难以落地。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 通过在 conftest.py 中利用 Pytest 的 pytest_addoption 与 pytest_collection_modifyitems 两个 Hook 钩子,可在不依赖第三方插件的情况下原地过滤包含 #cpu 等标签的测试用例。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 基于 Docstring 的 Opt-in 模式将代码修改成本降至最低,既不改变开发者的编写习惯,又天然保障了未标注测试在受限 CI 环境下的安全性。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 工程团队引入 GitHub CI 虚拟机后,由于环境仅有 CPU 而无 GPU,急需筛选并仅运行非 GPU 依赖的测试用例。 - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 按物理目录结构拆分测试并不现实,因为测试文件目录通常按业务功能划分,拆分会打破既有的模块组织且难以应对多维交错需求。 - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 使用 unittest.skipIf 等传统跳过装饰器存在样板代码膨胀的问题,且其逻辑是 Filter-out 而非 Opt-in,开发者漏标就会直接导致 CI 挂掉。 - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 测试套件往往存在硬件依赖与耗时维度(如几分钟的机器学习慢速测试与毫秒级快速测试)的正交分类需求,多重 skip 条件难以扩展。 - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 基于 unittest.TestCase 基类继承和 setUp 的方案具有强侵入性,会强制要求习惯编写函数式测试的开发者改造代码。 - [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 理想方案需具备 Opt-in 特性:默认不标注即不进受限 CI 运行,只有明确显式标注的用例才会被调度执行。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 最终方案采用在测试函数 Docstring 中添加类似社交媒体的标签(如 #cpu),对业务逻辑完全无副作用且书写零门槛。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) Docstring 属于 Python 原生元数据,即使标签书写错误也不会引发语法或运行时异常,提供了极高的容错性。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 在 conftest.py 中实现 pytest_addoption 钩子函数,可以方便地向 Pytest 注册 --cpu-only 等自定义命令行参数。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 通过 pytest_collection_modifyitems 钩子,Pytest 在收集完全部测试项后、执行测试前,允许原地遍历并过滤 items 列表。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 在钩子函数中直接检查测试函数的 __doc__ 是否包含指定 hashtag,未包含该标签的测试项会被安全移出执行队列。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 选用 hashtag 形式是因为常规 Docstring 几乎不会自然出现此类格式,兼具语义直观性与低冲突率,也可根据需要自定义唯一标识符。 - [03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 该钩子本质上是运行标准 Python 代码,未来可零成本扩展标签的交集、并集运算以及反向排除等更复杂的调度策略。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=0s) 业务背景与传统测试分类方案的痛点

码农高天在工作中遇到了一个测试分类的实际问题:代码仓库中的测试用例存在异构依赖,一部分测试必须依赖 GPU 才能执行,而另一部分测试只需要 CPU 即可运行。最近团队在 GitHub 上搭建了 CI 工作流,但 CI 提供的虚拟机只有 CPU 而没有配备 GPU,因此必须找到一种可靠的方法,在特定的 CI 环境下仅筛选并运行那些只需要 CPU 的测试。

面对这种需求,团队首先梳理了几种常见的解决思路,但都在实际工程落地中遇到了阻碍: - **按文件目录结构分类**:最直观的方式是将 GPU 测试与 CPU 测试分别存放于不同的文件或目录中,在 CI 流程中只执行对应的文件。然而在绝大多数成熟项目中,测试文件的目录层级已经用来承载业务功能模块的划分,同一个业务功能的测试集往往同时包含 CPU 和 GPU 用例。如果硬要利用物理文件路径做多维度的正交分类,不仅非常繁琐,而且得不偿失。 - **使用 `unittest.skipIf` 或跳过装饰器**:另一种方案是在用例上添加环境变量判断条件,环境不满足就自动跳过。但高天指出这种方式存在三大硬伤: 1. **样板代码膨胀**:每个测试函数都需要增加额外的检查代码。 2. **逻辑为 Filter-out 而非 Opt-in**:默认行为是全部执行,如果新加入团队的开发者写了一个需要 GPU 的测试却漏加了跳过标记,CI 就会直接报错挂掉。正如高天所强调的:“而我们更想要的是一个和它相反的逻辑,也就是所谓的 ‘opt in’,就你正常写你的测试无所谓,如果你的测试什么都没做我就认为它不能在CPU上跑,只有你这个测试做了一点事儿我才会在CI上跑这个测试。” 3. **可扩展性极差**:除了 CPU 与 GPU 的硬件维度,团队还有执行耗时的维度需求。许多与机器学习(Machine Learning)相关的测试执行缓慢,动辄需要几分钟,团队需要能够将一些运行飞快的用例标注为 Quick Test,以便开发者在本地边写边快速验证,开发完成后再跑完整慢速测试。若继续叠加多层 `skip` 条件,整体逻辑将变得极其复杂脆弱。 - **基于类结构的 `setUp` 与基类继承**:虽然可以通过把测试归入不同的 `TestCase` 类,利用 `setUp` 或 `setUpClass` 统一检测硬件,甚至编写一个通用的 Base Class,在基类的生命周期里实现 Opt-in 逻辑来减少重复代码,但这依然带来了强侵入性。它不仅要求所有测试都必须强制重构成面向对象的 `unittest.TestCase` 结构,而团队里部分成员习惯写独立的函数式脚本测试,这会为不熟悉该框架的开发者增加额外的改造成本。

```mermaid flowchart TD accTitle: 测试用例运行模式对比 accDescr: 对比传统 Filter Out 模式与期望的 Opt In 模式在处理新增测试时的逻辑差异。

subgraph FilterOut["传统 Filter-out 模式 (易挂 CI)"] A1["开发者新增 GPU 测试"] --> B1{"是否手动加了 skip 标记?"} B1 -- 否 --> C1["CI (仅 CPU) 默认执行该测试 --> CI 报错中断"] B1 -- 是 --> D1["成功跳过"] end

subgraph OptIn["理想 Opt-in 模式 (安全稳健)"] A2["开发者新增测试"] --> B2{"是否主动标注 #cpu?"} B2 -- 否 --> C2["CI (仅 CPU) 默认忽略 --> 安全通过"] B2 -- 是 --> D2["CI (仅 CPU) 纳入测试执行列表"] end ```

---

[03:40](https://www.youtube.com/watch?v=uPd8z0hT6qY&t=220s) 基于 Docstring 标签与 Pytest Hook 的轻量解法

在梳理完所有限制后,高天提炼出该方案的核心诉求:第一,维持现有的开发习惯,不能对开发者的日常编写流程产生侵入式改变;第二,提供一种极简、可选(Opt-in)的标注机制,标注了可在 CPU 跑的就进 CI,标注了 Quick 的就进快速测试套件。最终,他设计的交互接口非常轻巧:开发者只需在测试函数的 Docstring(文档字符串)中加入类似社交媒体的标签 `#cpu`。

```python def test_something(): """这是一个测试函数 #cpu""" assert True ```

由于 Python 的 Docstring 仅作为元数据存在,不参与任何底层执行逻辑,开发者可以极具安全感地在其中打上各种标签,即便写错也不会影响代码本身的语法与运行。而在 CI 或命令行中,只需通过 Pytest 传入自定义参数 `--cpu-only` 即可精确筛选。

高天随后详细拆解了该机制背后的底层实现方式。开发者无需引入复杂的第三方插件,只需在测试目录根路径或项目根目录下新建一个 `conftest.py` 文件,利用 Pytest 官方提供的两个核心 Hook 钩子函数即可完成全部逻辑: 1. **注册命令行参数**:通过实现 `pytest_addoption` 函数,向 Pytest 的命令行解析器中注册 `--cpu-only` 标志位。 2. **过滤收集到的用例**:通过实现 `pytest_collection_modifyitems(config, items)` 钩子函数。当 Pytest 完成测试目录扫描并收集到所有可用测试项(封装在 `items` 列表中)后,Pytest 会在实际运行前调用该钩子。此时,代码检查 `config` 中是否启用了 `--cpu-only` 选项;若未启用,则保持原样全量运行;若已启用,则遍历 `items` 列表,读取每个测试项对应函数对象的 Docstring(文档字符串),检查字符串内是否包含 `#cpu`。凡是不包含该标签的测试项全部被过滤丢弃,最后将筛选后的列表替换回原来的 `items`。

高天提到,利用这个钩子函数的本质就是在执行普通的 Python 代码,因此不仅能实现包含检查,还可以轻松扩展多标签交并集、选择 Filter-in 或 Filter-out 等任意复杂的定制策略。关于直接匹配 `#cpu` 是否会误伤正常文档内容的问题,高天解释道:“但是对于我们来说,docstring 里面是几乎不可能出现这种 hashtag 的形式的,这也是选择 hashtag 的一个原因,但是另一个原因是 hashtag 这东西现在本来就大量的被当做标签使用。”如果特定项目确实有特殊格式顾虑,也可以将标签设计得更具唯一性。最终,整套方案在框架侧仅需几行干净的 Hook 代码,在开发者侧只需写一个简单的 `#tag` 注释,既最大化减轻了维护负担,又优雅满足了异构硬件下的测试调度需求。

- **核心涉及的 Pytest Hook 函数**: - `pytest_addoption`:用于向命令行注入自定义参数选项(例如 `--cpu-only`)。 - `pytest_collection_modifyitems`:在用例收集完毕、执行开始前提供原地修改、过滤或重新排序用例列表 `items` 的能力。 - **Docstring 方案的工程收益**:零依赖侵入、完全保持原有函数式或面向对象写法、天然支持 Opt-in 渐进式标注、排查与代码可读性极高。

## Mentioned 码农高天, GitHub, CI, GPU, CPU, unittest, pytest, pytest_addoption, pytest_collection_modifyitems

## Video description 有的时候我们需要对我们的测试进行分类,然后在不同的环境和场合下运行不同的测试集。这次我和大家分享一下我在工作中遇到的一个真实问题,以及我最后给出的解决方案。

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

中文简报

码农高天借Pytest钩子与Docstring标签化解异构CI调度难题 通过在Docstring中嵌入标签并结合Pytest收集钩子,开发团队无需重构用例即可建立Opt-in过滤机制,根治传统跳过装饰器易漏配挂死CI的问题。 面对 GitHub CI 仅有 CPU 资源而仓库中混杂 GPU 依赖测试的场景,传统的按目录分流、`skipIf` 装饰器或基类继承方案存在样板代码臃肿、高侵入性及 Filter-out 模式易挂 CI 等硬伤。高天提出了一种利用测试函数 Docstring 标注 `#cpu` 标签的轻量解法,兼顾了快速测试等多维度扩展。在框架底层,只需在 `conftest.py` 中利用 Pytest 的 `pytest_addoption` 与 `pytest_collection_modifyitems` 钩子函数,便能在不破坏开发者原有代码习惯的前提下,以极少代码实现安全优雅的 Opt-in 动态测试集过滤。 针对 GitHub CI 虚拟机缺乏 GPU 的硬件限制,码农高天提出了一种基于文档字符串标签的轻量测试分类方案,有效解决了异构环境下的用例调度问题。 (0:00) 传统基于目录分类或 unittest.skipIf 装饰器的方案因多维冲突、侵入性强以及容易漏标导致 CI 挂掉等痛点,在实际项目中难以落地。 (0:00) 通过在 conftest.py 中利用 Pytest 的 pytest_addoption 与 pytest_collection_modifyitems 两个 Hook 钩子,可在不依赖第三方插件的情况下原地过滤包含 #cpu 等标签的测试用例。 (3:40) 基于 Docstring 的 Opt-in 模式将代码修改成本降至最低,既不改变开发者的编写习惯,又天然保障了未标注测试在受限 CI 环境下的安全性。 (3:40)

Reading summary

码农高天针对异构硬件环境下 CI 测试调度的痛点,提出利用 Docstring 中的 Hashtag 标签配合 Pytest Hook 函数的轻量级方案,以低侵入性的 Opt-in 机制替代传统的目录分类与条件跳过方案。 异构硬件测试的调度矛盾与传统方案缺陷 (0:00) 在 GitHub 上搭建的 CI 虚拟机仅配备 CPU,而代码仓库中部分机器学习相关测试强依赖 GPU,因此团队必须在 CI 环境中仅筛选运行 CPU 测试。若按文件目录做物理隔离,不仅破坏了以业务模块组织代码的原有结构,也难以兼顾耗时等多维度属性;若采用 unittest.skipIf 等跳过装饰器,其逻辑为默认全量执行的 Filter-out 模式,一旦开发者漏标跳过条件就会直接导致 CI 挂掉,且在叠加耗时等复杂条件时维护成本极高;而基于 Base Class 继承的面向对象重构则具有强侵入性,对习惯函数式测试的开发者并不友好。 Docstring 标签与 Opt-in 机制的工程设计 (3:40) 高天提炼出的核心诉求是维持开发习惯并实现 Opt-in 逻辑,即新测试默认不进 CI,仅当测试函数被显式标记时才会被纳入 CPU 测试集。团队采用的交互方案极为轻量:开发者只需在测试函数的 Docstring 中添加类似社交媒体的 `#cpu` 标签。由于 Docstring 仅作为元数据存在而不影响代码运行逻辑,即便书写有误也不会导致语法或执行崩溃,同时也能灵活扩展如针对快速验证的 Quick Test 标签。 基于 Pytest Hook 钩子的轻量底层实现 (3:40) 该方案无需引入第三方插件,仅需在根目录下 `conftest.py` 中编写两个 Pytest 官方 Hook 函数即可生效。首先利用 `pytest_addoption` 注册 `--cpu-only` 命令行标志位;随后利用 `pytest_collection_modifyitems(config, items)` 钩子,在测试收集完毕、执行开始前获取所有用例项。当启用筛选参数时,遍历并读取测试函数的 Docstring,丢弃不包含 `#cpu` 的用例并原地替换 `items`。高天指出,直接匹配 Hashtag 在日常文档中几乎不会发生误伤,且利用原生 Python 代码进行逻辑处理便于未来自由扩展多标签的交并集规则。 该方案以几行 Hook 代码实现了零依赖、无语法侵入的测试分类,保证了 CI 在无 GPU 环境下的稳健运行;对于有特定格式顾虑的项目,高天建议可将标签格式设计得更具唯一性以避免潜在冲突。

Original transcript

[0:00] 今天跟大家分享一个在工作中遇到的问题当然本期视频内容不会有任何的泄密啊请大家放心观看世界是这样的我们的代码里有一些测试这些测试有的需要GPU才能跑有的只用CPU就能跑而我们最近在Github上搭了一个CI,但是这个CI用的VM只有CPU,没有GPU那我们肯定还是希望在CI上面跑一些测试的可是那些需要GPU的测试我们就根本跑不了换言之我们需要一个方法去在某一个环境下跑那些只需要CPU的测试那做这件事有很多方案对吧我给大家先列举几个首先我们可以像这样把不同的测试放到不同的文件里那这些测试就自然完成了归类我们可以做到在CI上跑那些只需要CPU的测试文件但是在大多数情况下这种文件的结构已经被用作另外的分类了在大部分的项目里这种测试文件的结构是对测试内容的分类而很有可能同一个测试内容它既可以在CPU上跑也可以在GPU上跑所以想利用文件结构做多重分类难度是比较大的或者说也不一定是难度就是很麻烦得不偿失还有一个方案呢可以像这样我们可以利用unitest skipif之类的东西去判断一下当前环境是不是应该运行这个测试如果当前环境不满足啊就直接跳过这个测试然后这个方法有几个问题第一它意味着你很多单元测试都要额外增加不少的代码量第二,它是filter out的形式也就是说对于你这个测试系统不熟悉的人如果他自己写了一个需要GPU的测试它是必须要加上这一行的如果不加这一行,CI就会被break而我们更想要的是一个和它相反的逻辑也就是所谓的 "opt in"就你正常写你的测试无所谓如果你的测试什么都没做我就认为它不能在CPU上跑只有你这个测试做了一点事儿我才会在CI上跑这个测试第三这个方法的扩展性并不好我们实际上有两个分类需求最紧迫的是GPU和CPU的分类还有一个是测试够不够快大家知道有一些跟Machine Learning相关的测试是很慢的可能要好几分钟我们希望可以通过对一些比较快的测试进行标注让开发者在开发的过程中可以迅速跑一些很快的测试从而确定没有什么大问题等开发完了再跑一遍慢的测试那对于这种多层的分类你要怎么解决呢你再加一个 skip 吗你就会发现这个事情变得越来越复杂了那这里面的一些痛点啊可以通过其他的方式解决比如说啊我们可以在一个文件内把需要GPU的测试跟需要CPU的测试放到两个test case里这样我们可以利用 Setup 或者 Setup Class 参数去做一次对GPU的检测这可以减少重复代码的数量他甚至可以通过逻辑来解决那个opt in的问题我可以作为一个test case的base class然后要求每一个自己写的测试都去继承那个base class在那个base class setup里面去处理是不是要跳过这个测试但是第一它依然增加了一些代码量它还是需要进行一个归类第二这种方法要求你所有的测试必须要写到这个unit test.test case的框架下而我们组的一些人呢他希望自己可以直接通过脚本的方式运行某个函数也就是他写的测试往往不在这个Unitest的框架之下所以也是会给他们增加额外的工作量那前面说了这么多我们总结一下我们现在的需求最核心的需求其实就是两点第一对于所有人来说可以保持现有的开发流程不会对开发造成任何影响你原来怎么写现在还怎么写不会有问题第二允许所有人用非常简单的方式标注这个测试这件事是可选的如果你标注了它是CPU可以跑的东西他就会在CI上面跑如果你标注了这是一个Quick Test他就会在Quick的时候跑那么我最终提供的interface是这样的写测试的人可以在一个测试的boxstream里面加一个hashtag cpu而这个东西呢相当于给测试打了一个标签意思就是这个测试可以在CPU上运行我们支持多个标签也支持在Dogstream里面有东西的情况下继续打标签只要这个标签在Darkstream里面就行而我们知道Darkstream是不参与程序的运行的所以开发者可以非常放心的在Darkstream随便打个标签他知道这件事一定是非常安全的而当我们在CI上想跑那些只需要CPU就能运行的测试的时候我们只需要加一个--cpu only听起来是不是非常简单 [5:00] 其实也很简单 我们给大家介绍一下首先在你的测试文件夹里面增加一个conftest.py你在root里面加这个文件也行啊PyTest都找不着这个文件呢是PyTest提供的一个configuration的接口在这个文件里面你可以自己去设计各种hoes我们需要做的就两件事第一 写一个Pytest and Option函数在这个函数里面给PayTest增加一个option杠杆 CPU only第二,写一个Pytest Collection Modified Items函数,这个函数是Pytest提供的一个hook,是在Pytest找到所有可运行的测试之后,给你的一个修改运行哪些测试的机会。这个config里面保存了Pytest Configuration,而这个items就是一个list。这个list是Pytest收集到所有的可运行的测试我们对所有的测试进行一个便利如果用户定义了CPU only这个option我们就检查一下测试对应的函数的DarkStream看看#CPU是不是在这个DarkStream里如果不是的话我们就跳过这个test 否则我们就保留这个item最后把这个list替换成我们filter过的list这样我们就完成了在PyTest没有给CPUonly这个选项的时候我们拿到多少测试就运行多少测试而如果给了CPUonly我们就去查DogStream只有dogstring里面有这个#CPU我们才运行这个测试大家可以想到当我们有hook的时候我们在这里就可以完成非常复杂的逻辑了我们可以去处理多个hashtag我们可以选择是Future in还是Future out因为这个东西就是Python代码对吧你想做多复杂的逻辑都是可以的当然我们这是检查比较简单对吧我们只是查这个hashtag是不是在这个dogstream里那如果你要说啊这个Dog3里面本来就这个Hashack CPU怎么办那肯定是会不小心把它弄进来对吧如果你确实有这个顾虑啊你可以把这个label设计的更复杂一点但是对于我们来说啊Dogstream里面是几乎不可能出现这种hashtag的形式的这也是选择 hashtag 的一个原因但是另一个原因是hash tag这东西现在本来就大量的被当做标签使用我们经过这个修改之后就只有两个测试被运行了这就是两个带hash tag cpu的测试那我感觉这个方法无论是在Framework上面的修改还是对于开发者来说需要进行了修改都是代码量很少并且很容易读懂的所以在这里分享给大家啊希望能拓宽一下大家的思路没有人在自己的项目里也用得着对吧好那这一边是李啊喜欢的话记得点关注别忘了一件再见哦优优独播剧场——YoYo Television Series Exclusive