You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python patch装饰器执行顺序不符预期,patch.dict行为异常求解

关于unittest.mock中patch.dict嵌套装饰器的行为疑问

测试代码

import os
from unittest.mock import patch
 
def sys_exit_new1():
    print("sys_exit_new1:", os.environ.get("BANANA"))
def sys_exit_new2():
    print("sys_exit_new2:", os.environ.get("BANANA"))
 
@patch("sys.exit", new_callable=sys_exit_new1)
@patch.dict(os.environ, {"BANANA": "1"})
@patch.dict(os.environ, {"BANANA": "2"})
@patch("sys.exit", new_callable=sys_exit_new2)
@patch.dict(os.environ, {"BANANA": "3"})
def test_mytest(m1, m2):
    pass
 
test_mytest()

实际运行结果

sys_exit_new2: 2
sys_exit_new1: 2

官方文档说明

注意:装饰器从下往上应用,这是Python应用装饰器的标准方式。传入测试函数的mock对象顺序与此一致。

预期输出(原理解偏差)

sys_exit_new2: 3
sys_exit_new1: 1

原因解释

你遇到的问题核心是对patch装饰器的上下文执行时机和new_callable调用时机的误解,结合patch.dict的嵌套行为,具体拆解如下:

  1. 装饰器与上下文的执行逻辑
    Python装饰器确实是从下往上应用(离函数最近的先包装),但调用被装饰函数时,上下文的进入顺序是从上到下(最外层装饰器先生效),退出则是从下到上恢复。而patch的new_callable是在进入当前patch上下文后,进入内层装饰器上下文前被调用的。

  2. patch.dict的嵌套覆盖规则
    多个patch.dict作用于同一个字典时,会形成嵌套上下文:进入时内层设置会覆盖外层同键的值;退出时逐层恢复到上一层的状态。

  3. 你的代码执行步骤
    我们把装饰器从下到上编号,方便梳理:

    5. @patch("sys.exit", new_callable=sys_exit_new1)
    4. @patch.dict(os.environ, {"BANANA": "1"})
    3. @patch.dict(os.environ, {"BANANA": "2"})
    2. @patch("sys.exit", new_callable=sys_exit_new2)
    1. @patch.dict(os.environ, {"BANANA": "3"})
    

    调用test_mytest()时的实际流程:

    • 先进入最外层的装饰器(编号5)上下文,准备创建sys.exit的mock,此时会先进入内层的编号4和3的patch.dict上下文:
      • 编号4设置BANANA=1,紧接着编号3覆盖为BANANA=2;
      • 此时调用sys_exit_new1(),所以打印sys_exit_new1: 2;
    • 继续进入编号2的patch上下文,创建第二个sys.exit的mock,此时仍处于编号3的patch.dict上下文(BANANA=2),调用sys_exit_new2()后打印sys_exit_new2: 2;
    • 最后进入编号1的patch.dict上下文,设置BANANA=3,执行测试函数体;
    • 执行完毕后,逐层退出所有上下文,恢复os.environ的原始状态。
  4. 预期偏差的根源
    你误以为new_callable会在对应patch.dict的上下文内执行,但实际上:

    • sys_exit_new2的调用时机是在进入编号1的patch.dict(3)之前,此时生效的是编号3的patch.dict(2)设置;
    • sys_exit_new1的调用时机是在进入编号4和3的patch.dict之后,所以两者都打印BANANA=2。

内容的提问来源于stack exchange,提问作者Blazes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 16:22:01