我在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这是可怕的,因为它迫使我添加毫无意义的代码,因为函数签名变得更难理解-你必须实际阅读函数代码,而不是只看它的接口。
我遗漏了什么?有没有更好的方法来做这件事?
发布于 2013-02-02 18:08:02
带有默认值的函数参数仍然是位置参数,因此您看到的结果是正确的。当您为参数指定默认值时,您正在创建关键字参数而不是。当参数不是由函数调用提供时,仅使用默认值。
>>> 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再次发现引用argument的keyword,从而引发了一个错误。只有在分配了所有可能的位置参数之后,才会分配*args。
python2中的除了使用 `kwargs`**之外,没有其他方法可以定义只包含关键字的值。作为一种变通办法,您可以执行以下操作:
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.在python3中,您可以定义仅包含关键字的参数:
def my_function(a,b,c,*args, keyword, only=True): pass
# ... 请注意,仅关键字并不意味着它应该具有默认值。
发布于 2013-02-02 18:28:22
在Python中,非关键字参数不能出现在关键字参数之后,因此您尝试使用的函数签名是不可能的:
foo(my_keyword0="baa", my_keyword1="boo", 1, 2, 3, bar="baz", spam="eggs") # won't work如果你考虑到这一点,就会有非常充分的理由来解释这个限制(提示:关键字参数可以以任何顺序呈现,而位置参数是...好的,位置)
从documentation
在函数调用中,关键字参数必须跟在位置参数之后。传递的所有关键字参数必须与函数接受的参数之一匹配(例如,actor不是parrot函数的有效参数),并且它们的顺序并不重要。这也包括非可选参数
*args和**kwargs包含与现有形参不匹配的实参,因此一旦在实参列表中定义了spam=None,它就不会再传递给**kwargs:
当出现name形式的最后一个形式参数时,它会接收一个字典(请参阅映射类型-字典),其中包含所有关键字参数**,但与形式参数对应的关键字参数除外。这可以与*name形式的形式参数(在下一小节中描述)结合使用,该参数接收一个包含形式参数列表之外的位置参数的元组。(*name必须在**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.可以说,这可能比你最初的想法更具可读性,更不令人惊讶。
发布于 2013-02-02 17:44:08
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 argumenthttps://stackoverflow.com/questions/14660541
复制相似问题