← 返回题目列表

Python 猴子补丁是什么?使用时有什么风险?

高频 中等 第 3 / 27 题 更新于 2026/07/27
猴子补丁动态修改monkey patch测试

简化版

猴子补丁是在运行时动态修改模块、类或对象的属性和方法。它可以用于测试替身、兼容修复和框架扩展,但风险是行为隐蔽、影响全局、调试困难,生产代码中要非常谨慎。

详细版

示例:

class Service:
    def run(self):
        return "old"

def new_run(self):
    return "new"

Service.run = new_run

print(Service().run())  # new

这就是运行时替换类方法。

常见用途:

  • 测试中替换外部依赖;
  • 临时兼容第三方库 bug;
  • 框架在运行时注入能力;
  • 调试时替换函数行为。

风险:

  • 修改是全局生效的,可能影响其他代码;
  • 调用关系不明显,难排查;
  • 版本升级后补丁可能失效;
  • 多线程环境下时机更复杂;
  • 可能破坏库的内部假设。

测试里推荐使用 unittest.mock.patch 或 pytest 的 monkeypatch fixture,并控制补丁作用域。

完整版教学

一、为什么 Python 能猴子补丁

Python 是动态语言,模块、类、函数和对象大多都可以在运行时修改属性。

class User:
    pass

User.role = "guest"

也可以替换方法:

def hello(self):
    return "hello"

User.hello = hello

这给框架和测试带来很高的灵活性。

二、猴子补丁的典型例子

假设第三方函数:

def fetch_data():
    return request_remote_api()

测试时不想真的发网络请求,可以替换它:

def fake_fetch_data():
    return {"ok": True}

module.fetch_data = fake_fetch_data

这样被测代码调用 module.fetch_data() 时,实际走的是假函数。

三、测试中更推荐 patch 工具

手动替换容易忘记恢复:

old = module.fetch_data
module.fetch_data = fake
try:
    ...
finally:
    module.fetch_data = old

标准库提供了 unittest.mock.patch

from unittest.mock import patch

with patch("module.fetch_data", return_value={"ok": True}):
    ...

退出 with 后自动恢复,作用域更安全。

pytest 也常用 monkeypatch fixture:

def test_x(monkeypatch):
    monkeypatch.setattr(module, "fetch_data", fake_fetch_data)

测试结束会自动回滚。

四、生产代码中的风险

生产里随意猴子补丁很危险。

例如:

requests.get = my_get

这可能影响整个进程中所有使用 requests.get 的代码,包括你没想到的第三方库。

风险包括:

  • 全局影响范围难控制;
  • 调用链和真实实现不一致;
  • 并发环境下替换时机复杂;
  • 升级依赖后补丁失效;
  • 排查问题时很难从源码看出真实行为。

五、猴子补丁和依赖注入的取舍

很多场景可以用依赖注入替代猴子补丁:

class Service:
    def __init__(self, client):
        self.client = client

    def run(self):
        return self.client.fetch()

测试时传入 fake client:

service = Service(FakeClient())

这种方式依赖关系更清晰,不需要运行时偷偷改全局对象。

六、什么时候可以接受猴子补丁

相对合理的场景:

  • 单元测试中临时替换,且作用域受控;
  • 框架明确设计了 patch 扩展点;
  • 紧急绕过第三方库 bug,后续有移除计划;
  • 调试定位问题。

即使使用,也要写清楚原因、范围和恢复方式。

七、常见误区与追问

心法:猴子补丁是运行时改对象,不是正常扩展点。它的威力来自动态语言,风险也来自“别人看代码时看不到行为已经被换掉”。

  • 误区:猴子补丁就是 mock。 mock/patch 是测试工具里的受控替换,通常有作用域和自动恢复;猴子补丁泛指运行时修改对象行为,生产代码中风险更高。
  • 追问:为什么 patch 要 patch 使用处而不是定义处? 代码运行时查找的是被测模块当前绑定的名字;如果 service.pyfrom api import call,测试应 patch service.call,不是只 patch api.call
  • 误区:猴子补丁只影响当前函数。 如果改的是模块、类或全局对象,影响可能扩散到整个进程,其他线程或测试用例也可能看到被改后的行为。
  • 追问:生产环境什么时候能接受猴子补丁? 只有在临时兼容第三方 bug、框架明确要求或迁移过渡时谨慎使用,并要有注释、隔离范围和回滚方案。
  • 误区:猴子补丁比依赖注入更简单,所以应优先用。 依赖注入把可替换点显式暴露出来,更容易测试和维护;猴子补丁通常是没有设计扩展点时的补救。
  • 追问:测试后忘记恢复补丁会怎样? 后续测试可能在污染环境下运行,出现顺序相关的假阳性或假阴性;使用 unittest.mock.patch 上下文或 fixture 能降低风险。

八、加强记忆

猴子补丁就是运行时改对象行为,强在灵活,危险在隐蔽和全局影响。测试里用 mock.patch 或 pytest monkeypatch 控制作用域;生产里优先考虑依赖注入、配置扩展或正式封装,不要让补丁变成没人敢碰的暗门。