使用* args和** kws指定关键字参数

时间:2013-02-02 09:38:51

标签: python

我发现Python中的一种行为让我感到困惑和烦恼,我想知道我的错误。

我有一个函数应该使用任意数量的参数和关键字,但另外应该有一些默认值的关键字,它包含它的实际接口:

def foo(my_keyword0 = None, my_keyword1 = 'default', *args, **kws):
    for argument in args:
        print argument

问题在于,如果我尝试调用foo(1, 2, 3),我将只获得3的打印输出,值1和2将覆盖我的关键字参数。

另一方面,如果我尝试在*args之后或**kws之后移动我的关键字,则会导致语法错误。我发现问题的唯一解决方案是从**kws中提取关键字参数并为其设置默认值:

def foo(*args, **kws):
    my_keyword0 = None if 'my_keyword0' not in kws else kws.pop('my_keyword0')
    my_keyword0 = 'default' if 'my_keyword1' not in kws else kws.pop('my_keyword1')
    for argument in args:
        print argument

这很可怕,因为它迫使我添加无意义的代码,因为函数签名变得难以理解 - 你必须实际读取函数代码而不是只看它的界面。

我错过了什么?有没有更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:6)

具有默认值的函数参数仍然是位置参数,因此您看到的结果是正确的。为参数指定默认值时,创建关键字参数。当函数调用未提供参数时,仅使用默认值。

>>> def some_function(argument="Default"):
...     # argument can be set either using positional parameters or keywords
...     print argument
... 
>>> some_function()    # argument not provided -> uses default value
Default
>>> some_function(5)    # argument provided, uses it
5
>>> some_function(argument=5)    # equivalent to the above one
5
>>> def some_function(argument="Default", *args):
...     print (argument, args)
... 
>>> some_function()   #argument not provided, so uses the default and *args is empty
('Default', ())
>>> some_function(5)   # argument provided, and thus uses it. *args are empty
(5, ())
>>> some_function(5, 1, 2, 3)   # argument provided, and thus uses it. *args not empty
(5, (1, 2, 3))
>>> some_function(1, 2, 3, argument=5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: some_function() got multiple values for keyword argument 'argument'

请注意上一条错误消息:您可以看到1已分配给argument,然后python再次发现引用keyword的{​​{1}},从而引发一个错误。 仅在分配了所有可能的位置参数后才分配argument

在python2中,除了使用*args 之外,无法定义仅限关键字的值。作为一种解决方法,您可以执行以下操作:

**kwargs

在python3中,您可以定义仅限关键字的参数:

def my_function(a,b,c,d,*args, **kwargs):
    default_dict = {
        'my_keyword1': TheDefaultValue,
        'my_keyword2': TheDefaultValue2,
    }
    default_dict.update(kwargs)    #overwrite defaults if user provided them
    if not (set(default_dict) <= set('all', 'the', 'possible', 'keywords')):
        # if you want to do error checking on kwargs, even though at that
        # point why use kwargs at all?
        raise TypeError('Invalid keywords')
    keyword1 = default_dict['keyword1']
    # etc.

请注意,仅限关键字并不意味着它应具有默认值。

答案 1 :(得分:3)

在Python中,非关键字参数不能出现在关键字参数之后,因此您尝试使用的函数签名是不可能的:

foo(my_keyword0="baa", my_keyword1="boo", 1, 2, 3, bar="baz", spam="eggs")  # won't work

如果你考虑这个,有这个限制的非常有效的原因(提示:关键字参数可以按任何顺序呈现,位置参数是......井,位置)

来自documentation

  

在函数调用中,关键字参数必须遵循位置   参数。传递的所有关键字参数必须匹配其中一个   函数接受的参数(例如,actor不是有效的参数   对于鹦鹉功能),他们的顺序并不重要。这也是   包括非可选参数

*args**kwargs包含与现有形式参数不匹配的参数,因此,一旦在参数列表中定义spam=None,它就不会不再传递给**kwargs

  

当存在名称形式的最终形式参数时,它   接收包含所有内容的字典(请参阅映射类型 - 字典)   关键字参数**除了与正式对应的那些   参数。这可以与表格的形式参数结合使用   * name(在下一小节中描述)接收包含正式参数列表之外的位置参数的元组。   (*名称必须出现在**名称之前。)

与您(不可能)语法的最接近的近似值

def foo(my_keyword0=None, my_keyword1='default', numbers=None, **kwargs)

您将用作

foo(my_keyword0="bar", my_keyword1="baz", numbers=(1,2,3), spam='eggs')
foo("bar", "baz", numbers=(1,2,3))
foo("bar", "baz", (1,2,3))
foo("bar", "baz", (1,2,3), spam="eggs")
foo(numbers=(1,2,3))
foo(spam="eggs")
etc.
可以说,它可能比你原来的想法更具可读性和惊喜。

答案 2 :(得分:0)

def foo(my_keyword0 = None, my_keyword1 = 'default', *args, **kws):
    all_args = [my_keyword0, my_keyword1] + args + kws.values()
    for argument in all_args:
        print argument