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

为何修补基类的classmethod会对子类产生影响?

问题原因

当你使用mocker.spy(Foo, "wrapper")时,pytest-mock会将Foo.wrapper替换为一个Mock对象,这个Mock包裹的是已经绑定到Foo类的classmethod方法。子类Bar继承的wrapper实际指向的是Foo中被替换后的Mock,调用Bar.wrapper()时,Mock会直接调用绑定到Foo的原始wrapper方法,导致cls参数始终为Foo,因此两次调用都会创建Foo实例,最终calls结果变成["foo", "foo"]。

解决方案

方案一:Spy ClassMethod的原始函数

直接针对classmethod的原始函数进行spy,保留classmethod的descriptor结构,确保子类调用时能正确传递自身类作为cls参数:

def test_patching_parent_class(mocker):
    """
    pip install pytest pytest-mock
    """
    calls = []

    class Foo:
        def func2(self):
            calls.append("foo")

        @classmethod
        def wrapper(cls):
            cls().func2()

    class Bar(Foo):
        def func2(self):
            calls.append("bar")

    # 修改spy逻辑,针对原始函数而非绑定方法
    original_wrapper = Foo.wrapper.__func__
    spy = mocker.patch.object(Foo.wrapper, "__func__", wraps=original_wrapper)
    
    Foo.wrapper()
    Bar.wrapper()
    assert calls == ["foo", "bar"]

方案二:手动实现Spy逻辑

如果不想依赖patch的内部逻辑,可以手动替换classmethod的原始函数,自定义spy行为:

def test_patching_parent_class(mocker):
    """
    pip install pytest pytest-mock
    """
    calls = []

    class Foo:
        def func2(self):
            calls.append("foo")

        @classmethod
        def wrapper(cls):
            cls().func2()

    class Bar(Foo):
        def func2(self):
            calls.append("bar")

    # 手动实现spy,保留classmethod的绑定逻辑
    original_wrapper = Foo.wrapper.__func__
    def spy_wrapper(cls, *args, **kwargs):
        # 这里可以添加自定义的spy逻辑,比如记录调用参数
        return original_wrapper(cls, *args, **kwargs)
    Foo.wrapper.__func__ = spy_wrapper
    
    Foo.wrapper()
    Bar.wrapper()
    assert calls == ["foo", "bar"]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 02:17:12