
一直想聊聊 Python 测试这个话题。我见过太多项目功能写得飞起一上线就各种翻车修完一个 bug 带出三个新 bug。倒不是说大家不重视质量而是很多团队把测试当成了上线前的临时仪式——写几个断言应付一下覆盖率CI 里跑一遍就算完事。真正能把测试当成工程来做的少之又少。这篇内容围绕 Python 测试与质量保证从最基础的单元测试讲起一路聊到自动化测试、覆盖率、持续集成落地把我这些年踩过的坑、沉淀下来的操作习惯都翻出来。不管你是刚写 Python 不久的新人还是已经在维护中型项目的开发者只要想让自己的代码少出点幺蛾子这篇都值得你花十分钟看完。先说一个核心观点测试这件事越早做成本越低越往后拖代价越大。一个 bug 在写完代码一小时内被发现可能只需要五分钟修复如果流到生产环境被用户撞上光排查上下文就可能耗掉半天。所以不要抱着等代码稳定了再补测试的心态——代码永远不会自己变稳定只有测试能逼着它稳定。1. 整体思路与测试策略别一头扎进代码里写断言很多人开始搞测试时第一反应是我先把 unittest 用熟或者pytest 装好就开写。这个思路不能说错但缺了最关键的一步——先想清楚测什么、为什么测、测到什么程度算完。没有策略的测试写再多也是在给项目堆技术债。1.1 测试金字塔为什么单元测试必须占大头测试领域有个特别经典的分层模型叫测试金字塔底层是大量单元测试中间是少量服务层测试顶层是最少的端到端测试。这个比例不是拍脑袋定的而是基于成本和反馈速度的权衡。单元测试跑一次只要几十毫秒到几秒它能精确告诉你哪个函数、哪个逻辑分支出了问题。端到端测试一次可能要跑几分钟甚至更久而且一旦挂了你只能知道用户登录流程坏了具体哪一环出错还得一层层排查。我见过有的团队反过来搞端到端脚本写了几百个单元测试一个没有每次跑 CI 都像开盲盒红色一片却不知道从哪下手。所以我个人的习惯是新项目至少保证 60% 以上的行覆盖率核心业务模块能到 80% 以上。这里的覆盖不是看数字好看而是确保核心的规则、计算、数据处理逻辑都被真实断言保护住。1.2 先定边界再谈测试哪些代码值得写测试并不是所有代码都值得写测试。一个常见的误区是追求100% 覆盖率结果测试里全是无意义的断言改了实现就得跟着改测试反而拖慢了开发节奏。我一般会把代码分成三类核心业务逻辑——价格计算、状态流转、数据校验、权限判断这些必须写测试而且要把边界条件覆盖到位。胶水代码——配置读取、外部接口调用、日志封装这类代码逻辑简单写一个冒烟级别的测试保证它能通就行。框架本身的功能——比如 Django 的 ORM 查询、FastAPI 的路由映射这些框架作者已经测过了不值得你花时间重复覆盖。判断标准就一条这段代码出错后影响范围有多大影响越大测试优先级越高。按照这个标准去分配测试精力你会发现同样的时间测试的价值能翻好几倍。1.3 工具选型unittest、pytest 还是别的Python 官方的 unittest 也不是不能用但写起来实在太啰嗦了。同样的测试需求unittest 可能要写 20 行pytest 十几行就能搞定而且 pytest 的 fixture 机制、参数化、断言重写功能在写复杂测试时能省下大量体力。我现在的建议是除非你维护的是老项目必须跟着仓库里已有的 unittest 风格走否则新项目一律从 pytest 起步。它的插件生态也值得一夸pytest-cov 管覆盖率pytest-mock 管打桩pytest-xdist 还能并行跑测试这一套组合拳下来测试体验比 unittest 舒服太多。提示不要为了少装一个依赖而选 unittest。测试工具是给开发效率投资不是成本项。pytest 已经是 Python 测试的事实标准了你的同事接手时也会更舒服。1.4 质量保证不等于测试把检查点往前移质量保证和测试严格来说是两件事。测试是代码写完后的验证而质量保证是从需求分析、编码规范、代码评审到测试执行的一整套流程。我在项目里会刻意把一些检查点往前移让问题在进入测试阶段之前就被拦截掉。比如写代码之前先想清楚这段逻辑的输入有哪些边界值写代码的时候顺手把类型标注补上提交之前跑一遍 lint 和类型检查。这些看起来琐碎的习惯其实比测试本身更能提升代码质量。测试是兜底前面的习惯是让 bug 变少。2. 单元测试核心细节把 pytest 用到刀刃上搞清楚了策略下面进入实操层面。pytest 的开销极低但它有一些约定优于配置的机制用好了能让你的测试代码像讲故事一样清晰。我挑几个平时用得最多的功能展开说说。2.1 第一个测试函数从 assert 开始pytest 最让人舒服的一个设计是直接复用 Python 原生的assert语句不需要学一堆assertEqual、assertTrue之类的 API。它底层做了断言重写失败时能自动展开表达式的详细信息。# test_calculator.py def add(a, b): return a b def test_add_positive_numbers(): assert add(1, 2) 3 def test_add_negative_numbers(): assert add(-1, -1) -2跑一下看看效果pytest test_calculator.py -v你会看到每条测试的执行结果通过的打点失败的会展示断言比较的左右值。很多新手不知道的是pytest 默认会递归搜集当前目录下所有test_*.py或*_test.py文件函数名以test_开头的都会被自动识别。虽然也可以手动指定文件路径但按照约定组织文件能省掉很多配置。2.2 Fixture测试的依赖注入利器写测试时最烦的就是准备数据这一步。比如测试一个订单相关的函数需要先构造用户、构造商品、构造库存一套操作下来代码比测试本身还长。Fixture 就是为这个场景设计的。import pytest pytest.fixture def sample_order(): 构造一个标准订单对象供多个测试复用 return { id: 1, user_id: 42, items: [ {name: apple, price: 5, quantity: 3}, {name: banana, price: 3, quantity: 2}, ], } def test_calculate_total(sample_order): assert sample_order[items][0][price] * sample_order[items][0][quantity] 15 def test_order_has_user(sample_order): assert sample_order[user_id] 42fixture 最大的价值是按需取用。某个测试只需要用户数据就只声明需要的 fixture不需要的依赖压根不加载。这比 unittest 里每个 TestCase 都要写setUp的做法灵活得多。fixture 还有作用域的概念默认是 function 级别也就是每个测试函数都跑一遍构造。如果你的 fixture 构造起来特别耗时比如要初始化数据库连接可以加上pytest.fixture(scopesession)让整个测试会话只构造一次。但要注意session 级别的 fixture 如果有状态变化可能影响测试间的隔离能不用尽量不用。2.3 参数化同样的逻辑不同的输入业务逻辑里最常见的测试需求是同一个函数用多组输入分别验证。最笨的写法是复制粘贴几个测试函数但这会让测试代码冗余得要命。pytest 的参数化功能就是为这种场景设计的。import pytest from calculator import divide pytest.mark.parametrize( a,b,expected, [ (10, 2, 5), (9, 3, 3), (7, 2, 3.5), ], ) def test_divide_normal_cases(a, b, expected): assert divide(a, b) expected这组测试跑起来时会自动拆成三条独立的测试用例哪一组挂了你能立刻看到具体是哪组参数。我还习惯把预期抛出异常的输入也放到参数化里pytest.mark.parametrize(a,b, [(1, 0), (0, 0)]) def test_divide_by_zero_raises(a, b): with pytest.raises(ZeroDivisionError): divide(a, b)参数化不仅让代码更整洁更重要的是逼你把输入空间系统地想一遍——正常值、边界值、异常值——而不是想到哪写到哪。2.4 Mock 与 Monkeypatch隔离外部依赖到了写 Web 项目或涉及外部服务的时候单元测试最大的敌人是外部依赖。你的函数可能要请求第三方 API、读写数据库、发消息队列这些在测试环境里要么不可用要么不可控。Mock 就是把它们替身化不真的发请求而是假装接口返回了一个预期的结果。from unittest.mock import patch def fetch_user_name(user_id): # 某个真实场景中的外部调用 response requests.get(fhttps://api.example.com/users/{user_id}) return response.json()[name] patch(module.requests.get) def test_fetch_user_name(mock_get): mock_response mock_get.return_value mock_response.json.return_value {name: Alice} assert fetch_user_name(1) Alice mock_get.assert_called_once_with(https://api.example.com/users/1)这里的patch会把指定路径下的requests.get临时替换成 mock 对象。测试完毕自动恢复不会污染其他用例。pytest 里通常配合pytest-mock插件使用提供更简洁的mockerfixture。值得一提的是 unittest.mock 里有几个很实用但容易被忽略的参数 side_effect可以模拟函数抛异常或按顺序返回不同取值spec参数可以限制 mock 只能访问真实对象存在的属性防止你 mock 了一个根本不存在的接口字段等到联调时才暴露问题。2.5 不要为了覆盖率而写无意义断言常见的新手操作是写一个测试调用函数然后只 assert 它不抛异常。这种测试在覆盖率报告上看起来挺美但实际上什么都测不到。真正的断言应该落在函数的输出是否符合预期而不是函数没有被调用出错。我见过更夸张的情况有人为了让覆盖率数字好看把私有函数也拖出来测或者用# pragma: no cover注释把难以覆盖的分支直接屏蔽掉。这些操作对质量毫无帮助只是自我安慰。覆盖率数字是参考不是 KPI。它唯一的用处是告诉你哪块逻辑还没有测试保护而不是你的测试写得多好。3. 进阶质量手段覆盖率、静态分析与测试数据管理单元测试只是质量保证的起点。一个成熟的 Python 项目通常还要搭配覆盖率统计、代码规范检查、类型标注检查以及一套可复用的测试数据方案。这一套组合拳下来才能算有了比较完整的质量防线。3.1 覆盖率统计用数据发现盲区pytest 搭配pytest-cov插件跑一次就能出来一份覆盖率报告pip install pytest-cov pytest --covmyproject --cov-reportterm-missing--cov后面跟的是你要统计的源码目录--cov-reportterm-missing会顺便把哪些行没有被覆盖列出来。我平时还会加一个--cov-fail-under80让覆盖率低于 80% 的时候 CI 直接失败——这是把质量红线落到自动化里的手段。不过覆盖率真正有用的姿势是看哪个模块的覆盖低。比如你发现某个序列化模块覆盖率才 40%说明它有一堆分支逻辑从来没被测试触发过这就是明确的补测试信号。反过来某个工具类模块覆盖到了 95%你心里就有底了改动它的时候可以大胆重构。注意覆盖率只代表这段代码被执行过不代表这段代码被正确验证了。100% 覆盖率照样可能有逻辑错误它只是质量保障的一个维度。3.2 静态分析与格式检查让 CI 替你挑刺除了测试我会在 CI 里同时挂上 lint 和类型检查。Python 社区这几年工具迭代很快我目前的主力组合是ruff负责 lint 和格式mypy负责类型检查。pip install ruff mypy ruff check src/ tests/ mypy src/ruff 的速度极快而且是 Rust 写的格式化规范基本对齐 black。它能抓出未使用的导入、潜在的 bug 写法比如 None、没有 break 的循环、复杂度过高的函数等一堆问题。mypy 需要项目里有类型标注才能发挥作用所以我在新代码里会强制要求写函数签名类型存量代码逐步补齐。这俩工具的价值在于自动化评审人工 Code Review 的时间是稀缺资源应该留给架构、业务逻辑这类高层面的讨论而不是反复提醒别人你这里少了空行或者这里类型对不上。3.3 测试数据的组织别在测试里硬编码魔法数字测试代码里直接写一堆魔法数字短期看省事长期看是灾难。比如测试一个会员折扣计算直接断言result 85.0没人知道 85 是怎么来的。我习惯在测试文件头部定义清晰的数据构造用有业务含义的变量名。ORIGINAL_PRICE 100 DISCOUNT_RATE 0.85 EXPECTED_PRICE 85.0 def test_member_discount(): assert calculate_member_price(ORIGINAL_PRICE, DISCOUNT_RATE) EXPECTED_PRICE另一个常见痛点是测试数据互相污染。如果多个测试共用一份全局数据某个测试不小心改了它其他测试就会莫名其妙地挂。fixture 的 function 作用域就是为了解决这个问题每次测试都拿一份新鲜的数据隔离性是最好的。如果项目涉及数据库交互我建议测试环境单独用一个数据库跑测试前清空再准备数据运行完再清空。常用的方案有pytest-djangoDjango 项目、pytest-asyncioSQLAlchemy的事务回滚技巧FastAPI/异步项目本质都是保证每个测试用例运行在干净的状态下。3.4 快照测试与回归保护最近几年我越发觉得快照测试是个被低估的手段。它特别适合测那些“结果很复杂但你已经不关心具体细节”的场景比如一个函数返回一大段 JSON 配置你只需要确认它跟之前的行为保持一致就行。Python 里有syrupy这个快照库用起来非常傻瓜def test_generate_config(snapshot): config generate_config() assert config snapshot第一次跑的时候它会生成一份快照文件之后只要输出发生变化测试就会失败。如果变化是预期内的你可以用--snapshot-update参数更新快照。对于重构代码、升级依赖后的回归保护这招特别管用。4. 从本地测试到持续集成把质量门槛自动化本地跑测试只是第一步。代码迟早要合入主干、要发布上线如果每一步都靠人肉提醒提交前记得跑一下测试总会有疏忽的时候。持续集成CI的价值就是把质量门槛变成一条机器自动执行的流水线。4.1 为什么需要 CI堵住我这台机器上能跑的借口在我这跑得好好的啊是开发圈流传最广的段子但它背后是真问题本地环境和生产环境不一致依赖版本不同、系统库缺失、配置文件差异都能导致一个功能在本地健康、上线即崩。CI 提供的是一个干净的、可重复的、从零搭建的环境每次代码变更都会在这个环境里完整执行一遍安装依赖—跑测试—跑检查的流程。而且 CI 给团队带来一个非常宝贵的机制它替你说不。分支上 CI 是红的就不允许合并代码。这是一个客观的、不依赖个人记忆和自觉性的质量门槛。没有 CI 的项目合并代码基本靠胆量和交情——这次改动很小的你先合吧有问题后面修这句话我听得耳朵都起茧了。4.2 GitHub Actions 实操一个可用的 Python CI 配置下面是我个人比较常用的一份 Python 项目 CI 配置已经跑过不少项目稳定可靠。它做了几件事在干净环境里安装依赖、跑 lint 和类型检查、跑测试并上传覆盖率报告。name: CI on: push: branches: [main, develop] pull_request: jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.10, 3.11, 3.12] steps: - uses: actions/checkoutv4 - name: Set up Python uses: actions/setup-pythonv5 with: python-version: ${{ matrix.python-version }} - name: Cache dependencies uses: actions/cachev3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles(**/requirements*.txt) }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements-dev.txt - name: Lint and type check run: | ruff check src/ tests/ mypy src/ - name: Run tests with coverage run: | pytest --covmyproject --cov-reportterm-missing --cov-fail-under80 - name: Upload coverage report uses: actions/upload-artifactv4 with: name: coverage-report-${{ matrix.python-version }} path: htmlcov/说几个细节matrix让测试同时跑在多个 Python 版本上。如果你的项目声明支持 3.10 到 3.12那就别只在本地 3.12 上跑一遍就算数矩阵正好把这层差异暴露出来。缓存依赖是为了省时间但hashFiles的 key 要写对否则依赖一更新缓存就失效了。缓存失效宁可重新装也别误用旧的缓存。CI 的失败策略默认是任一阶段失败整个任务失败。我见过有人为了让 CI 跑完所有阶段再汇总结果写了复杂的if: always()判断但除非你有特殊需求默认行为就是最合理的。4.3 集成测试验证模块之间的握手单元测试测的是单个函数、单个模块它们之间能不能正确配合是另一回事。比如你的订单模块依赖库存模块两个模块单测都通过但真实调用时订单模块传的参数格式和库存模块期望的不一致——这种问题单测是测不出来的需要集成测试。集成测试的粒度通常介于单元测试和端到端测试之间。我一般会针对项目里的关键链路写集成测试比如订单创建 → 扣库存 → 推送消息完整走一遍不用启动整个前端界面但所有后端模块真实协作。在 pytest 里集成测试和单元测试可以放在同一个仓库里用目录或命名区分tests/ unit/ test_calculator.py integration/ test_order_flow.py跑 CI 的时候可以只跑单元测试快速反馈集成测试单独做一步或者在合并到主干时再跑。这样既保证了开发阶段的反馈速度又不会漏掉跨模块的验证。4.4 端到端测试最后一个安全网如果你的项目有 Web 界面端到端测试通常用浏览器自动化来跑。Python 生态里最常用的是Selenium或Playwright。这类测试能验证用户真实操作路径比如注册、登录、下单、支付从页面上操作一直到数据库落库。但端到端测试有两个让我又爱又恨的特点一是慢跑一次全量可能十几分钟二是脆稍微有一点网络波动、前端样式调整就可能红一片。所以我的策略是——端到端测试只覆盖最重要的几条核心路径数量控制在个位数并且只在 CI 的单独 stage 里跑。pytest tests/e2e -m e2e --tbshort --maxfail1用--maxfail1让它遇到第一个失败就停节省 CI 时间。反正核心路径挂了后面也不用看了先修再说。5. 常见问题与排查技巧这些年踩过的坑测试和 CI 本身是工具工具用起来必然有一堆坑。我把这几年被坑得最惨的几类问题整理成一份速查表希望能帮你少走一些弯路。5.1 环境相关装错 Python 版本、依赖装不进去这类问题在 CI 里最典型。本地跑得好好的推到 GitHub 上 CI 就报错一查是 Python 版本不同或者某依赖在最新 Python 版本上没有编译好的 wheel 包。我的经验是本地开发用的 Python 版本尽量和线上/CI 保持一致项目根目录放.python-version或runtime.txt明确声明。依赖锁定用requirements.txt把版本号写死或者干脆用pip-tools、poetry这类工具做依赖解析和锁定。别在 CI 里用pip install -r requirements.txt装一堆最新版本的包今天能过不代表明天还能过。编译型包比如pydantic、numpy在 CI 上如果报编译错误优先换用更高版本的 Python 或者带预编译包的镜像。5.2 测试不稳定flaky test 是 CI 的头号敌人flaky test 就是那种有时候过有时候挂的测试。它比一直失败的测试更可恶因为红色警报来得毫无规律时间久了大家都选择性失明连续几个月 CI 红灯都没人愿意点进去看。常见诱因和解决思路诱因典型表现解决思路测试之间数据污染单独跑每个都过一起跑就有失败检查是否共用全局状态每个测试独立清理数据时序/并发问题偶发超时、返回值顺序不一致用精确的 mock 代替真实等待别用time.sleep随机数/日期时间有时跟预期值不一样用freezegun冻结时间mock 掉随机源外部服务不稳定调用第三方接口时断时续单元测试里 mock 外部服务集成测试里用测试替身识别 flaky test 有个土办法同一个测试连续本地跑 20 遍。如果失败超过 1 次基本可以认定有隐藏的依赖问题别急着提交先查它依赖了哪些外部状态。5.3 测试越来越慢CI 从三分钟变成三十分钟测试数量是逐步累积的前期跑得快到了几百条用例时就开始明显变慢。解法有这么几个pytest-xdist并行跑pytest -n auto能自动分配 CPU 核数并行执行速度提升立竿见影。但要注意测试之间必须互相独立共享数据库的用例要提前设计好隔离。只跑有影响的测试pytest --lf跑上次失败的用例和pytest --ff失败优先在日常迭代时非常高效。CI 里可以配合pytest-testmon这类工具做增量测试但配置成本稍高。优化 fixture 作用域把重型的、只读的 fixture 从 function 作用域改成 session 作用域能省掉大量重复初始化时间。但改了作用域一定要确保 fixture 不会被某个测试偷偷改动。5.4 把 CI 当远程服务器用有人喜欢往 CI 脚本里塞各种调试命令打印环境变量、列目录、导出中间产物美其名曰排查问题。这种做法偶尔一次可以但不要养成习惯。CI 的脚本应该保持最小必要# 尽量精简 pytest --tbshort -n auto需要调试时在本地复现才是正路。实在复现不了可以临时在 CI 里加--pdb失败时进入调试器或者把PYTEST_ADDOPTS-s加上去看完整输出。一旦定位完问题记得立刻把这些调试开关撤掉别留在脚本里。5.5 pytest 不识别测试文件的坑pytest 默认是按test_*.py或者*_test.py的模式匹配文件名的。如果你的测试文件起名叫check_calculator.py函数也叫verify_calc哪怕内容写得再好pytest 也会视而不见然后非常迷惑地告诉你no tests ran。这个坑通常出现在从 unittest 迁移到 pytest 的旧项目里。解决方式有两种改文件名遵循约定或者在pytest.ini里配置自定义的匹配规则[pytest] python_files test_*.py *_test.py check_*.py python_functions test_* check_*但说真的能改文件名就别配规则。约定一旦被打破后面入坑的人就会越来越迷茫。6. 从项目角度再看质量保证测试的策略与节奏说了这么多具体工具和踩坑最后从项目整体落地的角度聊几句策略层面的东西。6.1 不要一次性铺开所有测试分阶段建设接到一个完全没有测试的老项目时最大的心理压力是那么多代码从哪里开始补。我的经验是分四个阶段走第一阶段先把 CI 配起来哪怕只跑一个最简单的pytest --version和一个冒烟测试。这一步的意义是让仓库里必须通过的质量关卡这个概念从无到有。第二阶段围绕最近出过 bug 的模块补回归测试。代码是会遗忘的这次修过的问题半年后重蹈覆辙的概率其实很高。回归测试就是给历史教训上个保险。第三阶段给核心业务模块补单元测试把覆盖率逐步拉到 60% 以上。第四阶段补集成测试和端到端测试覆盖关键链路。一个阶段做扎实了再进下一个。一口吃不成胖子但只要每个迭代都在往上垒质量半年后再回头看你会惊讶于项目的稳定性。有测试和没测试是天壤之别而有多少测试反而不那么关键了。6.2 别做测试的奴隶要让它为你服务写测试时最容易出现的心态失衡是测试至上——为了每个函数都配几个测试宁可把实现拆得七零八落甚至让生产代码去迁就测试的写法。这是本末倒置。测试是给你提供安全感的工具不是把你五花大绑的枷锁。如果一个测试的维护成本远超它所能捕获的问题那这个测试就是负资产。我清理过很多为了覆盖率而写的测试删掉之后开发效率反而提升了。关键判断标准依然是那句这段逻辑出错后影响有多大影响大的场景、复杂的分支逻辑、频繁变动的核心规则这些值得有测试保护而那些纯粹是传话的胶水代码别浪费时间。6.3 测试文件的可读性是对未来同事的尊重我最后想专门强调一下测试代码质量。很多人写生产代码讲究规范一到测试代码就放飞自我——变量名随意、断言散乱、数据准备逻辑堆得像意大利面。但测试代码也属于长期维护的代码它甚至更值得认真对待因为当项目出现问题的时候你第一反应一定是翻翻测试看看预期行为是什么。好的测试代码应该是可阅读的规格说明读一遍测试名你就知道这个模块的核心行为有哪些看一遍断言你就知道边界条件是怎么约定的。所以我会要求测试函数名称尽量描述业务场景而不是描述代码流程比如test_order_total_price_over_100_gets_shipping_discount就比test_calculate_price_normal好得多——前者是业务规则的表达后者只是个函数调用的记录。还有个容易被忽略的点测试里的数据构造要简洁但不含糊。一串数字[12, 7, 33]谁看了都懵换成[apple, banana, orange]一下子就清楚了。测试数据是会被反复阅读的它的可读性决定了整个质量体系的可维护性。6.4 把质量保证变成团队习惯而不是个人的正义感最后一个建议给带团队的朋友。有些人特别自律会自觉把测试写得很完善但如果整个团队没有同步建立这个习惯测试的维护就会变成孤军奋战——别人改了逻辑不更新测试CI 一红就说是那个测试写错了慢慢又回到了测试形同虚设的状态。要让质量保证持续起作用得把它变成团队共同遵守的规则比如 Pull Request 模板里加上测试影响勾选、Code Review 时把有没有配套测试作为硬性检查点、CI 里加上覆盖率门槛倒逼大家补测试。技术上的方案再好配合上机制和协作共识才能长久落地。我在实际推行这套流程的时候最大的体会是初期推进确实辛苦大家会抱怨测试拖慢了开发速度。但这个阵痛期只要扛过去项目就会进入一个正循环——回归 bug 快速暴露、重构有底气、发版不头大。到那时候你就会发现前期花在测试和 CI 上的每一分钟都在后面为你成倍地省回来。