在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
相关产品推荐
相关产品推荐

