使用functools.partial的Python装饰器中func参数来源问题
完整执行流程拆解
你看到的两次run_n调用,完全是带参数装饰器的固定执行逻辑,和partial的功能刚好匹配,拆解开每一步的运行顺序就很清楚:
- 第一步:遇到装饰器行
@run_n(count=2)时,Python会立刻执行这行的表达式,也就是带着count=2参数调用run_n。此时后面定义的那个打印Hello的func函数还根本没被创建,所以run_n的func参数会走到默认值None,对应你看到的第一组调试输出。 - 第二步:这次调用因为命中了
func is None的判断分支,没有返回装饰器最终需要的wrapper,而是返回了partial(run_n, count=count)生成的偏函数对象。
关于functools.partial的实际作用,不需要想得太复杂:它就是给目标函数预先固定一部分参数,生成一个新的可调用对象。后续调用这个新对象时,只需要传入剩下没固定的参数,它会自动把之前固定的参数拼接上,再调用原函数。
这里生成的偏函数,本质就是一个预先设好count=2的run_n版本:调用它的时候只需要传入func参数,它就会自动带着之前固定的count=2值,去调用真正的run_n。 - 第三步:按照Python装饰器的语法规则,@后面的表达式执行完得到的结果(也就是刚才的偏函数对象),会用来接收后面定义的被装饰函数。此时Python会把刚创建好的、打印Hello的
func函数,作为位置参数传给这个偏函数。
这时候就触发了第二次run_n的调用:偏函数自动把固定好的count=2带上,把传进来的func(真正的目标函数对象)作为第一个参数传给run_n,对应你看到的第二组调试输出里func打印为函数内存地址、count值保持2。 - 第四步:这次进入
run_n时func不再是None,就会走正常分支生成循环执行的wrapper返回。最终这个wrapper会被赋值给模块里的func变量,等你执行func()时就会按设定的次数循环打印Hello。
常见认知误区
很多人写带参装饰器时习惯用三层嵌套的写法:外层函数接收装饰器配置参数,返回一个内层函数,内层函数再接收被装饰函数。实际上根本不需要嵌套——只要@deco(args)执行后返回的,是一个能接收「被装饰函数」作为入参的可调用对象,就完全满足装饰器的运行要求。这里的partial就是帮你生成了那个用来接收被装饰函数的可调用对象,省掉了手写嵌套内层函数的冗余代码。
内容的提问来源于stack exchange,提问作者ScaryAardvark
相关产品推荐
相关产品推荐

