如何自动将实例属性作为参数传递给存储为实例属性的函数?
问题
我正在构建类A以执行特定统计操作,可通过自定义类方法将函数及其参数设为实例属性。我希望调用该函数时,能自动从实例属性中获取参数,而非手动传入。目前的run_function方法尚未实现此功能,示例代码如下:
import pandas as pd df = pd.DataFrame( [[1,2.,"a"],[0,8.,"q"],[1,3.5,"a"],[1,9.5,"g"]], columns=["binary","float","letters"] ) c = "binary" v = 1 def test_fn(df,c:str,v:list): if not isinstance(v,list): v = [v] return (df[c].isin(v)).sum() class A(): def __init__(self,df): self.df = df def _set_fn_args(self,**kwargs): self.__dict__.update(kwargs) def set_function(self,function,**kwargs): self.fn = function self._set_fn_args(**kwargs) def run_function(self,df=None,**kwargs): if df is None: df = self.df return self.fn(df,**kwargs) a = A(df) a.set_function(test_fn,c=c,v=v) a.__dict__.keys() # dict_keys(['df', 'fn', 'c', 'v']) a.fn # <function __main__.test_fn(df, c: str, v: list)> a.run_function(c=a.c,v=a.v) # 3
当前调用run_function需手动传入参数,失去了将参数存储为实例属性的意义。我希望调用run_function()时,能根据参数名称自动从实例a中获取c、v等必要参数。请问是否有简便实现方式,或是否有其他类编写方案可达成此目标?
解决方案
方法1:通过inspect模块自动匹配函数参数
利用inspect.signature获取目标函数的参数列表,自动从实例属性中提取对应参数,同时支持传入参数覆盖实例属性,通用性最强。
import pandas as pd import inspect df = pd.DataFrame( [[1,2.,"a"],[0,8.,"q"],[1,3.5,"a"],[1,9.5,"g"]], columns=["binary","float","letters"] ) c = "binary" v = 1 def test_fn(df,c:str,v:list): if not isinstance(v,list): v = [v] return (df[c].isin(v)).sum() class A(): def __init__(self,df): self.df = df def _set_fn_args(self,**kwargs): self.__dict__.update(kwargs) def set_function(self,function,**kwargs): self.fn = function self._set_fn_args(**kwargs) def run_function(self,df=None,**kwargs): if df is None: df = self.df # 获取函数的参数签名 sig = inspect.signature(self.fn) # 收集参数:优先用传入的kwargs,无则从实例属性取 fn_args = {} for param_name in sig.parameters: if param_name == 'df': fn_args['df'] = df else: fn_args[param_name] = kwargs.get(param_name, getattr(self, param_name)) return self.fn(**fn_args) a = A(df) a.set_function(test_fn,c=c,v=v) print(a.run_function()) # 输出3 # 传入参数覆盖实例属性 print(a.run_function(v=[0])) # 输出1
方法2:用functools.partial提前绑定参数
在set_function阶段就把实例的df和参数与目标函数绑定,后续run_function直接调用绑定后的函数即可,逻辑更简洁。
import pandas as pd from functools import partial df = pd.DataFrame( [[1,2.,"a"],[0,8.,"q"],[1,3.5,"a"],[1,9.5,"g"]], columns=["binary","float","letters"] ) c = "binary" v = 1 def test_fn(df,c:str,v:list): if not isinstance(v,list): v = [v] return (df[c].isin(v)).sum() class A(): def __init__(self,df): self.df = df self.bound_fn = None def set_function(self,function,**kwargs): # 绑定实例df和传入的参数 self.bound_fn = partial(function, self.df, **kwargs) def run_function(self,**kwargs): # 若传入新参数,合并原有绑定参数与新参数 if kwargs: temp_fn = partial(self.bound_fn.func, self.bound_fn.args[0], **{**self.bound_fn.keywords, **kwargs}) return temp_fn() return self.bound_fn() a = A(df) a.set_function(test_fn,c=c,v=v) print(a.run_function()) # 输出3 print(a.run_function(v=[0])) # 输出1
方法3:直接合并实例属性与传入参数
适合明确参数不会与实例其他属性冲突的场景,直接过滤实例中存储的函数参数,与传入参数合并后调用函数,实现成本最低。
import pandas as pd df = pd.DataFrame( [[1,2.,"a"],[0,8.,"q"],[1,3.5,"a"],[1,9.5,"g"]], columns=["binary","float","letters"] ) c = "binary" v = 1 def test_fn(df,c:str,v:list): if not isinstance(v,list): v = [v] return (df[c].isin(v)).sum() class A(): def __init__(self,df): self.df = df def _set_fn_args(self,**kwargs): self.__dict__.update(kwargs) def set_function(self,function,**kwargs): self.fn = function self._set_fn_args(**kwargs) def run_function(self,df=None,**kwargs): if df is None: df = self.df # 过滤实例中存储的函数参数(排除df、fn等核心属性) fn_kwargs = {k:v for k,v in self.__dict__.items() if k not in ['df','fn']} # 用传入参数覆盖实例参数 fn_kwargs.update(kwargs) return self.fn(df,**fn_kwargs) a = A(df) a.set_function(test_fn,c=c,v=v) print(a.run_function()) # 输出3 print(a.run_function(v=[0])) # 输出1
内容的提问来源于stack exchange,提问作者stat_is_quo
相关产品推荐
相关产品推荐

