函数间传递kwargs时,如何打印其内部设置的默认值
问题根源
你的装饰器只能从函数签名中提取参数默认值,但n_remove=0是在函数内部通过kwargs.get()设置的逻辑,不属于函数签名的一部分,所以装饰器无法自动识别这些隐式的默认值。
解决方案
方案1:显式声明kwargs参数到函数签名(推荐)
把函数内部用到的kwargs参数显式写到函数签名里并设置默认值,这样inspect模块就能直接识别到,装饰器可以轻松打印出所有参数的最终取值。
修改后的完整代码:
from functools import wraps import inspect def print_kwargs(func): @wraps(func) def wrapper(*args, **kwargs): print(f"Calling function {func.__name__}") # 打印传入的关键字参数 if kwargs: print("Passed keyword arguments:") for key, value in kwargs.items(): print(f" {key} = {value}") # 绑定参数并填充默认值,打印所有参数的最终取值 sig = inspect.signature(func) bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() print("Final argument values:") for key, value in bound_args.arguments.items(): print(f" {key} = {value}") return func(*args, **kwargs) return wrapper @print_kwargs def fun1(a, n_remove=0, **kwargs): b = fun2(a[n_remove:], n_remove=n_remove, **kwargs) return b @print_kwargs def fun2(x, n_remove=0, **kwargs): return x
效果
- 调用
fun1([1,2,3])时,会打印n_remove = 0 - 调用
fun1([1,2,3], n_remove=1)时,两个函数的n_remove取值都会被打印
优缺点
- ✅ 简单直观,依赖标准库
inspect的参数绑定功能,稳定可靠 - ❌ 需要修改函数定义,把内部用到的kwargs参数显式提出来
方案2:通过函数属性声明内部kwargs默认值
如果不想修改函数签名,可以给每个函数添加一个属性,存储内部用到的kwargs默认值,装饰器读取这个属性来打印实际取值。
完整代码:
from functools import wraps import inspect def print_kwargs(func): @wraps(func) def wrapper(*args, **kwargs): print(f"Calling function {func.__name__}") # 打印传入的关键字参数 if kwargs: print("Passed keyword arguments:") for key, value in kwargs.items(): print(f" {key} = {value}") # 打印函数内部声明的kwargs默认值 if hasattr(func, 'internal_kwargs_defaults'): print("Internal kwargs defaults:") for key, default_val in func.internal_kwargs_defaults.items(): actual_val = kwargs.get(key, default_val) print(f" {key} = {actual_val} (default: {default_val})") return func(*args, **kwargs) return wrapper @print_kwargs def fun1(a, **kwargs): n_remove = kwargs.get('n_remove', 0) b = fun2(a[n_remove:], **kwargs) return b # 给函数添加内部默认值属性 fun1.internal_kwargs_defaults = {'n_remove': 0} @print_kwargs def fun2(x, **kwargs): return x fun2.internal_kwargs_defaults = {'n_remove': 0}
效果
- 调用
fun1([1,2,3])时,会打印n_remove = 0 (default: 0) - 调用
fun1([1,2,3], n_remove=1)时,会打印n_remove = 1 (default: 0)
优缺点
- ✅ 无需修改函数签名,保持原参数结构
- ❌ 需要手动给每个函数添加属性,容易遗漏
方案3:解析函数源代码提取默认值(进阶)
如果不想手动声明,可以通过ast模块解析函数源代码,自动提取kwargs.get(key, default)中的键和默认值。这种方法依赖代码格式,但适合批量处理场景。
完整代码:
from functools import wraps import inspect import ast def extract_kwargs_get_defaults(func): """解析函数源代码,提取kwargs.get(key, default)的键和默认值""" source = inspect.getsource(func) tree = ast.parse(source) defaults = {} # 遍历函数体内的所有节点 for node in ast.walk(tree): # 匹配kwargs.get()的调用 if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == 'get' and isinstance(node.func.value, ast.Name) and node.func.value.id == 'kwargs'): if len(node.args) >= 1: key = ast.literal_eval(node.args[0]) default = ast.literal_eval(node.args[1]) if len(node.args) >=2 else None defaults[key] = default return defaults def print_kwargs(func): internal_defaults = extract_kwargs_get_defaults(func) @wraps(func) def wrapper(*args, **kwargs): print(f"Calling function {func.__name__}") # 打印传入的关键字参数 if kwargs: print("Passed keyword arguments:") for key, value in kwargs.items(): print(f" {key} = {value}") # 打印解析到的内部默认值 if internal_defaults: print("Internal kwargs defaults (parsed):") for key, default_val in internal_defaults.items(): actual_val = kwargs.get(key, default_val) print(f" {key} = {actual_val} (default: {default_val})") return func(*args, **kwargs) return wrapper @print_kwargs def fun1(a, **kwargs): n_remove = kwargs.get('n_remove', 0) b = fun2(a[n_remove:], **kwargs) return b @print_kwargs def fun2(x, **kwargs): foo = kwargs.get('foo', 'bar') return x
效果
自动解析出fun1的n_remove=0和fun2的foo='bar'并打印实际取值
优缺点
- ✅ 无需手动修改函数或添加属性,自动识别
- ❌ 依赖代码格式(必须是
kwargs.get(key, default)的写法),复杂表达式或嵌套场景会解析失败
内容的提问来源于stack exchange,提问作者Matias Andina
相关产品推荐
相关产品推荐

