Menu

Python *args 和 **kwargs:可变位置参数与关键字参数

*args 和 **kwargs 是什么意思,什么时候用,以及如何在函数之间干净地转发参数。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

不知道会有多少个参数的时候

大多数函数接收固定数量的参数。但偶尔你需要更灵活的写法:一个能接收任意多条消息的日志函数,一个把收到的参数原样转发给另一个函数的包装函数,一个接收配置但事先不知道调用者会传哪些选项的绘图函数。

Python 用参数列表中的两个特殊标记来处理这种情况:*args 和 **kwargs。

*args 收集位置参数

一个星号的意思是“把所有多出来的位置参数打包成一个元组”:

在函数内部,args 就是一个普通的元组。你可以遍历它、按索引取值、把它传给 len(),或者对它切片。

*args 通常和具名参数一起出现:

first 拿到第一个参数;*rest 把它后面的所有参数打包成一个元组。

**kwargs 收集关键字参数

两个星号对关键字参数做同样的事,把它们打包成一个字典:

在 describe 内部,kwargs 就是一个普通的字典。它的键是以字符串表示的关键字名称。

一起使用

你可以在同一个函数中同时使用两者。约定是 *args 写在 **kwargs 前面:

完整的参数顺序,从左到右是:

  1. 普通的位置参数(必填的或带默认值的)。
  2. *args。
  3. 仅限关键字参数(写在 *args 之后的参数都必须按关键字传入)。
  4. **kwargs。

title 拿到第一个位置参数。其余的位置参数都进入 tags。draft 必须按关键字传入(因为它写在 *tags 之后)。其他所有关键字参数都落进 metadata。

在调用处用 * 和 ** 解包

星号也可以反过来用:把一个序列或字典展开成一次调用的参数:

这在转发参数时特别好用:

wrapped 不需要知道 log 需要哪些参数。它只是把所有参数收集起来,再原样转发出去。这种写法在装饰器(一个更进阶的主题)和包装函数中随处可见。

什么时候不该用 *args 和 **kwargs

人很容易一兴奋就到处都用它们。有两点要注意:

它们会掩盖函数需要什么参数

如果你代码库里的每个函数都写成 def f(*args, **kwargs),调用方根本不知道哪些参数是有效的。能用具名参数就用具名参数,只让 *args/**kwargs 承载真正数量可变的输入,或者纯粹的参数转发。

错误信息会变得含糊

一个拼错的关键字名称,会在函数深处变成一个悄无声息的 None 或一个 KeyError,而不是在调用处立刻报出“unexpected keyword argument”。具名参数能给你好得多的反馈。

原则是:默认优先使用具名参数,只有当函数确实需要灵活,或者要把参数转发给另一个可调用对象时,才使用 *args/**kwargs。

一个实用的小例子

一个类似绘图辅助函数的工具,它用几个默认值包装了一个第三方函数:

*values 让调用者可以传入任意数量的值;**style 接住额外的配置,而不必把每个选项都写成具名参数。它很灵活,但并不晦涩,因为内部逻辑清楚地告诉你它会从 style 中读取哪些键。

小结

  • *args 把多出来的位置参数打包成一个元组。
  • **kwargs 把多出来的关键字参数打包成一个字典。
  • 在调用处,*seq 和 **dict 按相反的方向解包。
  • 参数必须按这个顺序出现:普通参数 → *args → 仅限关键字参数 → **kwargs。
  • 不要滥用:能用具名参数的时候,具名参数更清楚。

下一篇:lambda,一种在行内编写临时小函数的方法。

常见问题

Python 中的 *args 是什么?

*args 会把所有多出来的位置参数收集到一个元组中。写成 def f(*args):,你就可以调用 f(1, 2, 3),得到的 args 是 (1, 2, 3)。args 这个名字只是约定俗成,你可以随便起名,但大家都写 *args。

Python 中的 **kwargs 是什么?

**kwargs 会把所有多出来的关键字参数收集到一个字典中。写成 def f(**kwargs):,你就可以调用 f(name='Ada', age=30),得到的 kwargs 是 {'name': 'Ada', 'age': 30}。*args 和 **kwargs 一起使用,可以让函数接受任意组合的参数。

一定要命名为 args 和 kwargs 吗?

不用,关键在星号,不在名字。*values 和 **options 的效果完全一样。但 args 和 kwargs 几乎是 Python 代码中通用的约定,除非有特别的理由要用更具描述性的名字,否则就沿用它们。

Coddy 编程语言插图

和 Coddy 一起学编程

开始