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

在pandas DataFrame.apply中patch类方法失败,如何解决?

Pandas Apply 方法的 Mock 测试失败问题

我尝试为一个执行 pandas apply 操作的类编写测试,以下是简化版代码:

import pandas as pd
class Foo:
    def bar(self, row):
        return "BAR"

    def apply(self, df: pd.DataFrame):
        df["value"] = df.apply(self.bar, axis=1)

我编写的测试代码如下:

import unittest
from unittest.mock import patch

from my_module import Foo

class TestFoo(unittest.TestCase):
    def test_apply_1(self):  # 测试通过
        foo = Foo()
        df = pd.DataFrame([1])
        foo.apply(df)
        self.assertEqual(df["value"].values[0], "BAR")

    def test_apply_2(self):  # 测试失败
        foo = Foo()
        df = pd.DataFrame([1])  
        with patch.object(Foo, "bar") as mock_bar:              
            mock_bar.return_value = "BAZ"
            foo.apply(df)
            self.assertEqual(df["value"].values[0], "BAZ")

    def test_apply_3(self):  # 测试失败
        foo = Foo()
        df = pd.DataFrame([1])
        with patch.object(foo, "bar") as mock_bar:
            mock_bar.return_value = "BAZ"
            foo.apply(df)
            self.assertEqual(df["value"].values[0], "BAZ")

运行测试时,test_apply_2 和 test_apply_3 失败,报错:

ValueError: No objects to concatenate

这表明补丁未正常工作,请问我哪里出错了?


问题原因与解决方案

核心问题

问题出在 Foo.apply 方法里的 df.apply(self.bar, axis=1)——这里的 self.bar 是绑定方法,在实例初始化时就已经和当前实例绑定。当你用 patch.object mock bar 方法时,df.apply 引用的还是初始化时绑定的原始方法,mock并未生效。同时,mock方法如果没有正确接收参数,会导致pandas无法拼接返回值,触发ValueError。

修复方案

方案1:修改原类,避免直接引用绑定方法

调整Foo的apply方法,通过类调用bar并传入实例,这样mock类的bar方法就能生效:

import pandas as pd
class Foo:
    def bar(self, row):
        return "BAR"

    def apply(self, df: pd.DataFrame):
        # 改用lambda包装,调用类的bar方法并传入self
        df["value"] = df.apply(lambda row: Foo.bar(self, row), axis=1)

此时test_apply_2可以正常通过,因为lambda调用的是被mock的Foo.bar。

方案2:不修改原类,正确mock绑定方法

确保mock的是实例上的绑定方法,并且让mock方法正确接收row参数:

def test_apply_3_fixed(self):
    foo = Foo()
    df = pd.DataFrame([1])
    # 使用side_effect指定mock方法,确保接收row参数
    def mock_bar(row):
        return "BAZ"
    with patch.object(foo, "bar", side_effect=mock_bar):
        foo.apply(df)
        self.assertEqual(df["value"].values[0], "BAZ")

或者用wraps参数保留原方法的参数签名:

def test_apply_3_fixed(self):
    foo = Foo()
    df = pd.DataFrame([1])
    with patch.object(foo, "bar", wraps=foo.bar) as mock_bar:
        mock_bar.return_value = "BAZ"
        foo.apply(df)
        self.assertEqual(df["value"].values[0], "BAZ")

关于ValueError的解释

当mock的bar方法没有正确接收row参数时,pandas遍历每行调用该方法会返回不符合要求的结果,导致无法生成有效的Series赋值给df["value"],从而抛出No objects to concatenate错误。确保mock方法能接收row参数并返回单个值,即可解决这个问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 13:36:11