Python中如何让带额外参数的foo继承bar的函数参数定义?
问题描述
现有两个Python函数:
def foo(code:int, *args, **kwargs): if code == 1: bar(*args, **kwargs) else: return None def bar(a:str, b:int, c:int=1, d:int=2): pass
希望用户调用foo时,能看到与直接定义如下时一致的参数提示信息:
def foo(code:int, a:str, b:int, c:int=1, d:int=2): if code == 1: bar(a, b, c, d) else: return None
尝试过ParamSpec但因为foo比bar多一个code参数无法满足需求,请问如何让foo继承bar的参数定义?
解决方案
方法1:用Concatenate + ParamSpec(Python 3.10+,类型检查友好)
Python 3.10新增的Concatenate可以把额外前置参数和目标函数的参数合并,刚好适配foo多一个code参数的场景,能让类型检查器和IDE正确识别参数提示:
from typing import ParamSpec, Concatenate, TypeVar, Callable P = ParamSpec("P") R = TypeVar("R") def bar(a: str, b: int, c: int = 1, d: int = 2) -> None: pass def foo(code: int, *args: P.args, **kwargs: P.kwargs) -> R | None: if code == 1: return bar(*args, **kwargs) return None # 标注foo的类型,让类型检查器识别完整参数 FooType = Callable[Concatenate[int, P], R | None] foo: FooType = foo
方法2:用inspect动态修改签名(运行时参数提示)
如果需要在运行时让foo的参数信息和bar完全对齐(比如help(foo)显示正确参数),可以用inspect模块获取bar的参数结构,再修改foo的签名:
import inspect from inspect import Parameter, Signature def foo(code:int, *args, **kwargs): if code == 1: bar(*args, **kwargs) else: return None def bar(a:str, b:int, c:int=1, d:int=2): pass # 获取bar的参数列表 bar_params = list(inspect.signature(bar).parameters.values()) # 插入code参数到最前面 code_param = Parameter( 'code', Parameter.POSITIONAL_OR_KEYWORD, annotation=int ) # 生成新签名并赋值给foo foo.__signature__ = Signature(parameters=[code_param] + bar_params)
方法3:装饰器封装(灵活复用)
写一个通用装饰器,自动继承目标函数的参数同时添加前置参数,适合多个函数需要复用参数定义的场景:
import inspect from inspect import Parameter, Signature def inherit_params_from(target_func, prepend_params=None): def decorator(func): target_params = list(inspect.signature(target_func).parameters.values()) new_params = [] # 处理前置参数 if prepend_params: for name, annotation, default in prepend_params: param_default = default if default is not inspect._empty else inspect._empty new_params.append(Parameter( name, Parameter.POSITIONAL_OR_KEYWORD, annotation=annotation, default=param_default )) new_params.extend(target_params) func.__signature__ = Signature(parameters=new_params) return func return decorator def bar(a:str, b:int, c:int=1, d:int=2): pass @inherit_params_from(bar, prepend_params=[('code', int, inspect._empty)]) def foo(code:int, *args, **kwargs): if code == 1: bar(*args, **kwargs) else: return None
内容的提问来源于stack exchange,提问作者PaleNeutron
相关产品推荐
相关产品推荐

