我发现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
这很可怕,因为它迫使我添加无意义的代码,因为函数签名变得难以理解 - 你必须实际读取函数代码而不是只看它的界面。
我错过了什么?有没有更好的方法来做到这一点?
答案 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
如果你考虑这个,有这个限制的非常有效的原因(提示:关键字参数可以按任何顺序呈现,位置参数是......井,位置)
在函数调用中,关键字参数必须遵循位置 参数。传递的所有关键字参数必须匹配其中一个 函数接受的参数(例如,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