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

单元测试替换类基类触发TypeError,求排查与解决方法

问题分析与解决方案

错误原因

你直接将原Bar类的__dict__复制到新创建的类中,导致__init__方法里的super()仍绑定着原Bar类的上下文。Python 3中无参数的super()会隐式使用方法定义时的类(即继承自Foo的原Bar),当新Bar实例调用该方法时,super()等价于super(原Bar, self),但新Bar实例并非原Bar的子类或实例,因此触发类型错误。

解决方案

方案一:临时修改原类的基类(最简单)

不需要创建新类,直接临时替换Bar的__bases__属性,测试完成后恢复:

class Foo:
    pass

class Bar(Foo):
    def __init__(self):
        super().__init__()

import pytest

@pytest.fixture
def uut():
    global Bar
    old_bases = Bar.__bases__
    # 临时将Bar的基类改为object
    Bar.__bases__ = (object,)
    try:
        yield Bar
    finally:
        # 测试结束后恢复原基类
        Bar.__bases__ = old_bases

def test_Bar(uut):
    b = uut()

这种方式会让Bar的__init__方法中的super()动态指向当前基类object,调用super().__init__()不会有任何问题。

方案二:创建新类时重新定义__init__

如果必须创建新类,需要为新类重新定义__init__,避免复用原方法的绑定上下文:

class Foo:
    pass

class Bar(Foo):
    def __init__(self):
        super().__init__()

import types
import pytest

@pytest.fixture
def uut():
    global Bar
    old_bar = Bar
    # 为新类定义独立的__init__方法
    def new_init(self):
        super().__init__()
    # 复制原Bar的其他属性,但排除原__init__
    class_attrs = {k: v for k, v in Bar.__dict__.items() if k != '__init__'}
    new_bar = types.new_class(Bar.__name__, (object,), {}, lambda ns: ns.update(class_attrs))
    new_bar.__init__ = new_init
    try:
        yield new_bar
    finally:
        Bar = old_bar

def test_Bar(uut):
    b = uut()

新的__init__方法是为新类定义的,super()会正确指向新类的基类object。

补充说明

如果你尝试过用mock.Mock作为基类,同样会遇到相同问题——只要复用原Bar的__init__方法,super()就会绑定原类上下文。上述两种方案都能解决这个核心问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 05:52:45