Python多层嵌套函数传递kwargs时如何完整透传全部参数
问题场景
开发模块化2D绘图程序时存在大量嵌套函数调用,需要实现将关键字参数字典完整向下传递到多层嵌套函数的能力。
初始实现的缺陷
初始写法通过**kwargs自动收集当前层未使用的参数向下传递,但上层函数已经解包使用的参数不会保留在kwargs中,无法传递到下层函数,示例代码如下:
mydict = {'a':1,'b':2,'c':3} def bar(b, c, **kwargs): print("bar b=" + str(b)) print("bar c=" + str(c)) print("bar kwargs: ", kwargs) def foo(a, b, **kwargs): print("foo a=" + str(a)) print("foo b=" + str(b)) print("foo kwargs: ", kwargs) bar(b, **kwargs) foo(**mydict)
运行输出:
foo a=1 foo b=2 foo kwargs: {'c': 3} bar b=2 bar c=3 bar kwargs: {}
可以看到foo中使用的参数a、b没有传递到bar层。
现有方案的不足
目前想到的方案是将原始字典同时作为位置参数和解包参数传入,未使用的冗余参数通过**_接收,但是这种写法重复传参不够优雅:
mydict = {'a':1,'b':2,'c':3} def bar(mydict, b, c, **_): print("bar b=" + str(b)) print("bar c=" + str(c)) print("bar 'kwargs': ", mydict) def foo(mydict, a, b, **_): print("foo a=" + str(a)) print("foo b=" + str(b)) print("foo 'kwargs': ", mydict) bar(mydict, **mydict) foo(mydict, **mydict)
该方案运行输出符合预期,但重复传参的写法冗余度高。
更优实现方案
推荐两种可读性高、无冗余传参的实现方式,适配不同的代码场景:
方案1:显式传递配置对象(最适配模块化绘图场景)
对于2D绘图这类有明确层级配置的场景,不要依赖零散的kwargs透传,直接将完整参数字典作为单独的配置参数(通常命名为config/options/params)在每一层显式接收和传递:
mydict = {'a':1,'b':2,'c':3} def bar(config, b=None, c=None, **kwargs): # 优先使用显式传入的参数,缺省从config取,支持灵活覆盖参数 b = b if b is not None else config['b'] c = c if c is not None else config['c'] print("bar b=" + str(b)) print("bar c=" + str(c)) print("bar full config: ", config) # 下层调用继续传递config即可 # lower_func(config, **kwargs) def foo(config, a=None, b=None, **kwargs): a = a if a is not None else config['a'] b = b if b is not None else config['b'] print("foo a=" + str(a)) print("foo b=" + str(b)) print("foo full config: ", config) # 向下传递时只需要传config,不需要重复解包全量字典 bar(config, **kwargs) # 调用时只需要传一次配置字典,不需要重复传参 foo(mydict, **mydict)
- 优势:
- 没有重复传参的冗余写法
- 每一层都能拿到完整的配置,同时保留kwargs的灵活性,支持调用时单独覆盖某个参数
- 如果不需要支持参数覆盖,甚至不需要在函数签名里显式列出需要的参数,直接从config中取值即可,新增参数时完全不需要修改各层函数签名
- 非常适配模块化绘图场景,不同绘图组件可以按需从config中取自己需要的参数,后续扩展参数继承(比如子图继承父图配置)、默认值配置也非常方便
方案2:kwargs合并透传(适配轻量嵌套场景)
如果不想单独传递配置对象,可以在每一层调用下层函数前,将当前层使用的显式参数合并回kwargs再传递,不需要额外传递原始字典:
mydict = {'a':1,'b':2,'c':3} def bar(b, c, **kwargs): print("bar b=" + str(b)) print("bar c=" + str(c)) print("bar full kwargs: ", {**kwargs, 'b':b, 'c':c}) # 继续向下传递时合并当前层参数即可 # lower_func(**{**kwargs, 'b':b, 'c':c}) def foo(a, b, **kwargs): print("foo a=" + str(a)) print("foo b=" + str(b)) # 合并当前层的a、b到kwargs,再传给下层 full_kwargs = {**kwargs, 'a':a, 'b':b} print("foo full kwargs: ", full_kwargs) bar(**full_kwargs) foo(**mydict)
运行输出:
foo a=1 foo b=2 foo full kwargs: {'c': 3, 'a': 1, 'b': 2} bar b=2 bar c=3 bar full kwargs: {'a': 1, 'b': 2, 'c': 3}
- 说明:Python 3.9+版本可以用更简洁的
kwargs | {'a':a, 'b':b}写法替代{**kwargs, 'a':a, 'b':b}实现字典合并。 - 优势:不需要修改原有函数的调用方式,不需要额外传递配置参数,逻辑直观。
- 劣势:每一层都需要手动写合并逻辑,参数多的时候代码会有冗余,适合嵌套层级不深、参数数量少的场景。
不推荐的做法
不要为了透传全量参数去修改函数栈帧、或者用全局变量存储参数字典,这类写法会严重破坏代码的可调试性和可读性,后续维护成本极高。
对于模块化2D绘图程序,优先选择方案1,把绘图相关的所有参数(线宽、颜色、坐标、渲染层级等)统一放到配置对象中传递,长期维护成本最低。
内容的提问来源于stack exchange,提问作者Stiin
相关产品推荐
相关产品推荐

