Python位置参数与关键字参数的性能差异及底层原理分析
CPython 位置参数与关键字参数的性能差异原理说明
你测试得到的耗时排序完全符合CPython的参数处理逻辑,速度从快到慢为 调用1 > 调用2 > 调用3 > 调用4 > 调用5,背后的核心差异来自CPython运行时参数绑定阶段的不同处理流程。
各调用耗时差异的底层原因
CPython的函数参数绑定逻辑完全在运行时执行(因Python动态特性,编译期无法确定函数签名是否会被替换、装饰器修改,无相关编译优化),不同传参方式的开销差异如下:
- 调用1(
fun1(1,2,3))最快:所有参数都是位置参数,调用时直接按顺序压入调用栈,函数帧拿到参数后直接按索引取值,不需要做任何名称匹配、额外校验,开销最小。 - 调用2(
fun2(1,2,3))略慢于调用1:fun2定义了仅限位置参数a和带默认值的参数c,参数绑定阶段需要多做两步操作:校验传入位置参数数量是否符合最低要求、匹配未传参数的默认值,因此比无默认值的全位置参数调用开销略高。 - 调用3(
fun2(1,2,c=3))慢于调用2:只要传入关键字参数,CPython就需要先把关键字参数打包为临时键值对结构,在绑定阶段逐个匹配函数定义的参数名,同时校验参数是否重复、是否存在无效参数名,单关键字参数会带来固定的匹配开销。 - 调用4(
fun2(1,b=2,c=3))慢于调用3:关键字参数的匹配开销和关键字数量正相关,2个关键字参数需要多做一次名称匹配校验,因此耗时更高。 - 调用5(
fun2(1,2,*d))耗时最高:动态参数解包(包括*解包可迭代对象为位置参数、**解包字典为关键字参数)无法在编译期做任何预校验,运行时需要额外执行:遍历解包对象、校验解包内容合法性、合并到已传入的位置/关键字参数队列、避免参数冲突,这类操作的开销远高于静态传参。
更高开销的典型场景
还有两类传参场景会带来比上述测试更明显的性能损耗:
- 函数定义使用
*args+**kwargs接收任意参数:参数绑定阶段不会做参数名匹配直接打包到args和kwargs中,但如果函数内部需要从kwargs中按键取值,会额外增加字典查询开销;如果同时混用静态传参+动态解包,还会多出参数合并的开销。 - 大量关键字参数+多层
**解包混用:比如函数有10个以上参数,调用时部分传关键字、部分来自2个以上字典的**解包,运行时需要多次遍历字典、校验参数冲突,开销会线性上升。
注:这类差异的绝对耗时非常小,只有在单函数被调用千万次以上的场景才会体现出可感知的差异,普通业务开发完全不需要为此调整写法。
内容的提问来源于stack exchange,提问作者Grismar
相关产品推荐
相关产品推荐

