裸星号在参数列表中的作用是什么?"仅关键字"参数又是什么意思?

485
裸星号在函数参数中的作用是什么?
当我查看pickle模块时,我看到了这个:
pickle.dump(obj, file, protocol=None, *, fix_imports=True)

我知道单个和双个星号可以用来表示参数(用于变量数量的参数),但是这里没有任何前置内容。而且我很确定这与pickle无关。这可能只是一个示例。我只是在将这个发送给解释器后才知道它的名字。
>>> def func(*):
...     pass
...
  File "<stdin>", line 1
SyntaxError: named arguments must follow bare *

如果有关系的话,我正在使用Python 3.3.0版本。

有关编程的内容翻译为中文。仅返回已翻译的文本:有关代码示例的解释,请参见:https://dev59.com/2nA85IYBdhLWcg3wCfHm#14298976 - Charlie Parker
6个回答

426

Bare * 用于强制调用者使用命名参数 - 因此,当您没有以下关键字参数时,不能定义一个带有 * 作为参数的函数。

更多详情,请参见这个答案Python 3文档


有关编程的内容翻译成中文。仅返回翻译后的文本:详细的代码示例及解释请见:https://dev59.com/2nA85IYBdhLWcg3wCfHm#14298976 - Charlie Parker
1
但是为什么 def foo(a, *, **kwargs) 会返回相同的语法错误? - ado sar
@ado sar:有意思。我刚在这里找到了关于它的讨论:https://bugs.python.org/issue2613 - outa

120

虽然原回答完全回答了问题,但只是添加了一点相关信息。单个星号的行为源自PEP-3102。引用相关部分:

The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:

    def compare(a, b, *, key=None):
        ...

简单地说,这意味着要传递键的值,您需要明确将其作为key="value"进行传递。


5
哦,这让事情变得更清晰了。其实拥有一个参数 * 就像拥有一个参数 args*,但由于你没有给它命名,它的唯一作用可能是悄悄地吞噬所有剩余的位置参数,以强制剩余的参数成为仅限关键字的参数。 - Stephen
18
@Stephen 我最初也认为,裸体 * 的作用是吞噬剩余的位置参数,但事实并非如此。传递比函数期望的位置参数更多的额外位置参数会导致这种错误:foo()需要1个位置参数,但给出了2个 - Ajay M
我会在“作为一个方便的快捷方式,我们可以简单地省略'ignore'名称,意味着'不允许超出此点的任何位置参数'”之前添加PEP示例。那个等效的示例清楚明确地说明了它的作用。 - Horror Vacui

73
def func(*, a, b):
    print(a)
    print(b)

func("gg") # TypeError: func() takes 0 positional arguments but 1 was given
func(a="gg") # TypeError: func() missing 1 required keyword-only argument: 'b'
func(a="aa", b="bb", c="cc") # TypeError: func() got an unexpected keyword argument 'c'
func(a="aa", b="bb", "cc") # SyntaxError: positional argument follows keyword argument
func(a="aa", b="bb") # aa, bb

使用 **kwargs 的上述示例

def func(*, a, b, **kwargs):
    print(a)
    print(b)
    print(kwargs)

func(a="aa",b="bb", c="cc") # aa, bb, {'c': 'cc'}

69

从语义上讲,它表示其后的参数是仅关键字参数,因此如果您尝试提供一个未指定名称的参数,将会出现错误。例如:

>>> def f(a, *, b):
...     return a + b
...
>>> f(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given
>>> f(1, b=2)
3

实际上,这意味着您必须使用关键字参数调用函数。通常在没有参数名称提示就很难理解参数目的时使用。

例如比较sorted(nums, reverse=True)sorted(nums, True)。后者可读性要差得多,因此Python开发人员选择让您以前一种方式编写。


30

假设你有一个函数:

def sum(a,key=5):
    return a + key 
您可以用两种方式调用此函数: sum(1,2) 或者 sum(1,key=2) 假设您希望只能使用关键字参数来调用函数sum
您可以在函数参数列表中加入*来标记位置参数的结尾。
因此,函数定义如下:
def sum(a,*,key=5):
    return a + key 

只能使用sum(1,key=2)来调用


-3

我发现以下链接对于解释**args**kwargs非常有帮助:

https://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/

基本上,除了上面的答案之外,我从上面的网站(来源:https://pythontips.com/author/yasoob008/)学到了以下内容:

在下面首先定义演示函数后,有两个例子,一个使用*args,另一个使用**kwargs

def test_args_kwargs(arg1, arg2, arg3):
    print "arg1:", arg1
    print "arg2:", arg2
    print "arg3:", arg3

# first with *args
>>> args = ("two", 3,5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5

# now with **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two","arg1":5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3

*args 允许您动态构建参数列表,按照它们被提供的顺序进行接收,而 **kwargs 可以启用传递 NAMED 参数,并且可以根据 NAME 进行处理(无论它们被提供的顺序如何)。

该网站继续指出,参数的正确排序应为:

some_func(fargs,*args,**kwargs)

4
这个回答几乎与问题无关。它甚至使用了一个已经过时的 Python 版本,这个版本没有这个功能。 - Antti Haapala -- Слава Україні

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接