编程Python 开发者

解释一下 Python 函数中 *args 和 **kwargs 是如何工作的。它们用于什么,错误使用时有什么危险,以及如何将其正确地与普通参数和命名参数结合起来?

用 Hintsage AI 助手通过面试

答案

*args — 收集任意数量的位置参数到一个元组中。

**kwargs — 收集任意数量的命名参数(键=值)到一个字典中。

这使得可以编写具有灵活接口的函数:

def my_func(a, b, *args, **kwargs): print(a, b) print(args) print(kwargs) my_func(1, 2, 3, 4, x=10, y=20) # 1 2 # (3, 4) # {'x': 10, 'y': 20}

细节:

  • 普通参数在最前面,然后是 *args,接着是带默认值的命名参数,最后是 **kwargs — 在函数声明中必须严格遵循这个顺序。
  • 可以通过 * "解包" 序列,而通过 ** 可以在调用函数时解包字典。
  • 不能重复定义同样的参数 — 将引发 TypeError 错误。

有陷阱的问题

可以交换 *args 和具有默认值的命名参数的顺序吗,例如:

def foo(a, *args, x=10, **kwargs): pass

或者默认值是否必须总是在 *args 之后?

答案:

在 Python 3 中可以!像这样(在 Python 2 中不工作):

def foo(a, *args, x=10): pass

*args 后面的参数("仅限关键字参数")可以具有默认值或是必需的 — 只能通过名称指定。

由于不熟悉该主题的细节而导致的实际错误示例


故事

通过 *args/**kwargs 传递错误的参数

在 REST API 服务中,数据编组的自动化是通过 **kwargs 完成的。多余的参数没有被注意到 — 函数在 kwargs 中接受了意外的值,导致控制逻辑丢失,并出现难以调试的错误。


故事

命名参数重复

在调用函数时,开发者意外地既通过显式参数又通过 **kwargs 指定了参数,例如:my_func(a=1, **{"a": 2})。结果是 — TypeError 和服务崩溃。


故事

忘记了位置参数或仅命名的参数

在编写装饰器时,不正确的参数传递(未遵循顺序和结构)导致原始函数接收到的参数数量不正确,协议发生变化,因此在生产中开始出现 TypeError 调用错误。