为何无法将装饰后的函数传入scipy.integrate.ode?
为什么装饰后的函数传入scipy.integrate.ode时,包装函数的*args为空?
将装饰后的微分方程函数传入scipy.integrate.ode后,包装函数被调用时*args是空的,导致原函数因缺少t和y参数报错,具体情况如下:
可正常运行的代码
y0, t0 = 1, 0 def dydt(t, y): return y*0.5 r = scipy.integrate.ode(dydt) r.set_initial_value(y0) r.integrate(10) assert r.successful()
无法运行的装饰器版本代码
y0, t0 = 1, 0 def dec_func(func): def wrap_func(*args, **kwargs): return func(*args, **kwargs) return wrap_func @dec_func def dydt(t, y): return y*0.5 r = scipy.integrate.ode(dydt) r.set_initial_value(y0) r.integrate(10) assert r.successful()
报错信息:TypeError: dydt() missing 2 required positional arguments: 't' and 'y',打印len(args)结果为0。
补充测试(使用functools.wraps仍报错)
from functools import wraps y0, t0 = 1, 0 def dec_func(func): @wraps(func) def wrap_func(*args, **kwargs): return func(*args, **kwargs) return wrap_func @dec_func def dydt(t, y): return y*0.5 r = scipy.integrate.ode(dydt) r.set_initial_value(y0) r.integrate(10) assert r.successful()
原因分析
scipy.integrate.ode内部会通过读取函数的__code__.co_argcount属性来判断参数数量,进而确定调用时是否传入t和y。
- 普通函数的
__code__.co_argcount会正确返回参数个数(比如示例中的dydt返回2); - 装饰后的包装函数定义为
*args, **kwargs,其__code__.co_argcount为0,即使使用functools.wraps也仅复制函数名、文档字符串等元数据,不会修改这个参数计数属性; ode看到参数计数为0,就不会传入t和y,直接调用包装函数,最终导致原函数因缺少参数报错。
解决办法
方法1:显式匹配原函数参数签名
直接让包装函数的参数与原函数完全一致,确保co_argcount符合ode的预期:
def dec_func(func): def wrap_func(t, y): return func(t, y) return wrap_func
方法2:动态适配任意参数的函数
如果需要装饰不同参数签名的函数,可以用inspect模块动态生成匹配参数的包装函数:
import inspect def dec_func(func): sig = inspect.signature(func) params = list(sig.parameters.keys()) # 动态生成与原函数参数一致的包装函数 code = f"def wrap_func({', '.join(params)}): return func({', '.join(params)})" local_vars = {'func': func} exec(code, globals(), local_vars) wrap_func = local_vars['wrap_func'] # 复制原函数元数据 wrap_func.__name__ = func.__name__ wrap_func.__doc__ = func.__doc__ return wrap_func
内容的提问来源于stack exchange,提问作者carpanaut
相关产品推荐
相关产品推荐

