Python文档函数签名中的单独星号*代表什么含义?
问题:Python函数定义中单独的
*参数有什么作用? 编辑注:本问题曾被标记为讨论*args/**kwargs参数解包问题的重复问题,但正如原提问说明的,此处的单独星号用法与列表/字典解包无关,属于独立问题。
在阅读subprocess模块文档时会发现,不少方法的参数中包含一个单独的*参数,例如subprocess.run的签名如下:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs)
最初容易误以为这个符号表示它之前的位置参数(本例中为args)可以在关键字参数前重复传入任意次数,但这个理解是错误的。比如subprocess.Popen构造函数的参数列表里也存在单独的*,但它位于带默认值的参数pass_fds之后,其签名如下:
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, group=None, extra_groups=None, user=None, umask=-1, encoding=None, errors=None, text=None, pipesize=-1)
搜索相关内容时返回的结果几乎全是关于*args和**kwargs用法的内容,查阅源码也很难直接明确这个单独星号的作用。
回答
Python 3 函数定义中出现的单独*是仅限关键字参数(keyword-only argument)分隔符,是PEP 3102引入的语法,作用非常明确:
- 所有出现在
*之后的参数,调用函数时必须通过关键字传参的方式传入,不能按位置顺序传入。 - 这个符号本身不是实际参数,不会接收任何传入值,仅作为参数传参规则的边界标记存在。
可以用一个极简示例快速验证:
def demo(a, b, *, c): print(a, b, c) # 合法调用:*后的c用关键字形式传入 demo(1, 2, c=3) # 非法调用:c按位置传入会直接抛出TypeError # 报错信息:demo() takes 2 positional arguments but 3 were given demo(1, 2, 3)
对应到提到的两个subprocess方法:
subprocess.run里*紧跟在args之后,意味着只有第一个args参数支持按位置传参,后面的stdin、stdout、cwd、check等所有参数,都必须写成参数名=值的关键字形式传入,不能按位置顺序书写。subprocess.Popen里*出现在pass_fds=()之后,意味着pass_fds及之前的参数都可以按位置传入,但是后面的group、extra_groups、user等参数必须用关键字形式传入。
这个语法的设计目的很明确:
- 对于参数多、可选配置项多的函数,强制关键字传参可以避免按位置传参时容易出现的顺序错乱、值传错的问题,大幅提升代码可读性,减少低级bug。
- 后续迭代调整函数参数顺序时,只要
*之后的参数名保持不变,就不会破坏已有调用代码的兼容性。
注意它和*args的核心区别:*args会收集所有多余的位置参数存入元组供函数内部使用,而单独的*不会收集任何参数,会直接拦截所有尝试传给后续参数的位置值,触发类型错误。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

