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

如何避免重复代码注册Pandas Series/DataFrame访问器?

为Pandas Series/DataFrame批量添加访问器方法并保留原函数签名

问题背景

需要通过Pandas的register_series_accessor和register_dataframe_accessor为Series和DataFrame批量绑定自定义方法,避免手写重复的访问器样板代码。现有实现可完成功能,但生成的方法签名为通用的*args, **kwargs,无法匹配原函数的参数信息;同时DataFrame版本需要在原函数参数基础上额外添加axis关键字参数(默认值0)。

核心实现思路

  1. 使用functools.wraps保留原函数的元数据(文档字符串、名称等)
  2. 借助inspect模块解析原函数签名,动态调整包装函数的签名:
    • Series场景:移除原函数第一个参数(对应传入的Series对象)
    • DataFrame场景:移除原函数第一个参数后,追加axis关键字参数

完整代码实现

1. 定义核心操作函数

import pandas as pd
import inspect
from functools import wraps

def mf_cumsum_plus_const(ser, const=0):
    """对Series做累加后加上常数
    参数:
        const: 要添加的常数,默认0
    返回:
        处理后的Series
    """
    return ser.cumsum().add(const)

def mf_cumprod_plus_const(ser, const=0):
    """对Series做累乘后加上常数
    参数:
        const: 要添加的常数,默认0
    返回:
        处理后的Series
    """
    return ser.cumprod().add(const)

MYFUNC_LIST = [mf_cumsum_plus_const, mf_cumprod_plus_const]

2. Series访问器实现

@pd.api.extensions.register_series_accessor('mf')
class MFSeriesAccessor:
    def __init__(self, pandas_obj):
        self._obj = pandas_obj
        for func in MYFUNC_LIST:
            self.tack_on_function(func)

    def tack_on_function(self, func):
        # 提取方法名(去掉前缀mf_)
        new_name = '_'.join(func.__name__.split('_')[1:])

        @wraps(func)
        def inner(*args, **kwargs):
            # 绑定当前Series对象到原函数的第一个参数
            return func(self._obj, *args, **kwargs)

        # 调整签名:移除原函数的第一个参数(ser)
        sig = inspect.signature(func)
        params = list(sig.parameters.values())[1:]
        inner.__signature__ = sig.replace(parameters=params)

        inner.__name__ = new_name
        setattr(self, new_name, inner)

3. DataFrame访问器实现

@pd.api.extensions.register_dataframe_accessor('mf')
class MFDataFrameAccessor:
    def __init__(self, pandas_obj):
        self._obj = pandas_obj
        for func in MYFUNC_LIST:
            self.tack_on_function(func)

    def tack_on_function(self, func):
        # 提取方法名(去掉前缀mf_)
        new_name = '_'.join(func.__name__.split('_')[1:])

        @wraps(func)
        def inner(*args, axis=0, **kwargs):
            # 对DataFrame的指定轴应用原函数
            return self._obj.apply(func, axis=axis, args=args, kwargs=kwargs)

        # 调整签名:移除原函数第一个参数,追加axis关键字参数
        sig = inspect.signature(func)
        params = list(sig.parameters.values())[1:]
        # 添加axis参数,仅允许关键字传递,默认值0
        axis_param = inspect.Parameter(
            'axis',
            inspect.Parameter.KEYWORD_ONLY,
            default=0
        )
        params.append(axis_param)
        inner.__signature__ = sig.replace(parameters=params)

        inner.__name__ = new_name
        setattr(self, new_name, inner)

验证效果

# 测试Series访问器
ser = pd.Series([1, 2, 3])
print("Series测试结果:")
print(ser.mf.cumsum_plus_const(const=5))
# 查看方法签名(会显示正确的参数信息)
print("\nSeries方法签名:")
help(ser.mf.cumsum_plus_const)

# 测试DataFrame访问器
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
print("\nDataFrame测试结果:")
print(df.mf.cumsum_plus_const(axis=0, const=5))
# 查看方法签名(包含axis参数)
print("\nDataFrame方法签名:")
help(df.mf.cumsum_plus_const)

运行后,IDE的代码补全和help()都会显示正确的参数列表,而非通用的*args, **kwargs。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 04:36:09