Blog

Python 中的 *args 和 **kwargs

Python 里的单星号和双星号到底做了什么,参数必须按什么顺序排列,以及怎样把参数原样转交给另一个函数。

参数前面加一个星号,会把多出来的位置参数收集成元组。加两个星号,会把多出来的关键字参数收集成字典。

argskwargs 这两个名字只是约定俗成,真正起作用的是星号。

单星号收集位置参数

在函数内部,args 就是普通的元组。没有传多余的参数时,它是空的。

# *args collects positional arguments
def total(*args):
    return sum(args), type(args).__name__

print(total(1, 2, 3))
print(total())

输出:

(6, 'tuple')
(0, 'tuple')

双星号收集关键字参数

kwargs 就是普通的字典,顺序和传参时一致。

# **kwargs collects keyword arguments
def describe(**kwargs):
    return kwargs, type(kwargs).__name__

print(describe(name='ada', age=36))

输出:

({'name': 'ada', 'age': 36}, 'dict')

顺序是固定的

先是普通参数,然后是 *args,接着是仅限关键字参数,最后是 **kwargs。Python 强制要求这个顺序。

# the order is fixed
def mixed(first, *args, key=None, **kwargs):
    return first, args, key, kwargs

print(mixed(1, 2, 3, key='k', extra=True))

输出:

(1, (2, 3), 'k', {'extra': True})

*args 后面的参数只能按名字传。

单独一个星号强制使用关键字参数

如果你不需要额外的位置参数,但希望调用方写明参数名,就单独放一个星号。

# a bare star forces keyword-only arguments
def connect(host, *, port=5432, timeout=30):
    return host, port, timeout

print(connect('localhost', port=5433))
try:
    connect('localhost', 5433)
except TypeError as err:
    print(type(err).__name__ + ':', err)

输出:

('localhost', 5433, 30)
TypeError: connect() takes 1 positional argument but 2 were given

只要函数有好几个选项,就值得这么做,因为 connect(host, 5433, 60) 让读代码的人一头雾水。

调用时,星号用来解包

在调用时,星号的意思正好相反:把序列或字典展开成参数。

# unpacking at the call site
def point(x, y, z):
    return x + y + z

coords = [1, 2, 3]
named = {'x': 1, 'y': 2, 'z': 3}
print(point(*coords), point(**named))

输出:

6 6

把参数全部转交出去

所有包装函数和装饰器背后都是这个模式:什么参数都接收,原封不动地转交。

# forwarding everything to another function
def logged(fn, *args, **kwargs):
    print('calling', fn.__name__, 'with', args, kwargs)
    return fn(*args, **kwargs)

print(logged(point, 1, 2, z=3))

输出:

calling point with (1, 2) {'z': 3}
6

包装函数不需要知道被包装函数的签名。

要点

  • *args 是多余位置参数组成的元组,**kwargs 是多余关键字参数组成的字典。
  • 顺序是:普通参数、*args、仅限关键字参数、**kwargs
  • 单独一个 * 会让它后面的参数只能按关键字传。
  • 在调用处,星号负责解包,而不是收集。

如果你发现自己在一个不是包装函数的函数上写了 *args, **kwargs,通常最好把参数一个个写出来。函数签名本身就是文档。

这篇文章对你有帮助吗?

点一颗爱心来评分!

平均评分 0 / 5. 投票总数: 0

还没有人投票。来做第一个评分的人吧。