使用 * args、 * * kwargs 和可选/默认参数调用 Python 函数

在 Python 中,我可以定义如下函数:

def func(kw1=None,kw2=None,**kwargs):
...

在这种情况下,我可以将 func调用为:

func(kw1=3,kw2=4,who_knows_if_this_will_be_used=7,more_kwargs=Ellipsis)

我还可以将函数定义为:

def func(arg1,arg2,*args):
...

可以称之为

func(3,4,additional,arguments,go,here,Ellipsis)

最后,我可以组合这两种形式

def func(arg1,arg2,*args,**kwargs):
...

但是,不起作用的是:

func(arg1,arg2,*args,kw1=None,kw2=None,**kwargs):  #SYNTAX ERROR (in Python 2 only, apparently this works in Python 3)
...

我最初的想法是,这可能是因为函数

def func(arg1,arg2,*args,kw1=None):
...

可以被称为

func(1,2,3)  #kw1 will be assigned 3

因此,这将引入一些模棱两可的3是否应该打包到 argskwargs。但是,对于 Python3,只能指定关键字参数:

def func(a,b,*,kw=None):  # can be called as func(1,2), func(1,2,kw=3), but NOT func(1,2,3)
...

这样看来,似乎就不存在与以下内容的句法歧义了:

def func(a,b,*args,*,kw1=None,**kwargs):
...

但是,这仍然会带来一个语法错误(使用 Python3.2进行了测试)。我为什么会失踪呢?还有,有没有一种方法可以得到我上面描述的行为(使用带默认参数的 *args)——我知道我可以通过操作函数内的 kwargs字典来模拟这种行为。

53196 次浏览

可以在 Python3中执行此操作。

def func(a,b,*args,kw1=None,**kwargs):

只有当您想要指定只包含关键字的参数 没有时,才使用裸 *,而 没有接受数量可变的位置参数。你不能用两个 ABC。

引用语法,在 巨蟒2中,你有

parameter_list ::=  (defparameter ",")*
(  "*" identifier [, "**" identifier]
| "**" identifier
| defparameter [","] )

而在 巨蟒3,你有

parameter_list ::=  (defparameter ",")*
(  "*" [parameter] ("," defparameter)*
[, "**" parameter]
| "**" parameter
| defparameter [","] )

其中包括在 *参数之后添加其他参数的规定。

更新:

最新的 Python 3文档 给你

如果要同时使用这两个参数,请记住 * args 和 * * kwargs 必须是指定的最后一个参数。

def func(arg1,arg2,*args,kw1=None,kw2=None,**kwargs): #Invalid
def func(arg1,arg2,kw1=None,kw2=None,*args,**kwargs): #Valid

这些注释似乎是基于将函数定义的构造方式与将提供的参数分配回定义中指定的参数的方式混为一谈。

这是这个函数的定义,它有6个参数。 它是通过在函数调用中向其传递命名和未命名参数来调用的。

举个例子。 在调用函数时,当一个参数被命名时,它的提供可能是无序的。 Arg1和 arg2是强制参数,如果没有作为命名参数传递给函数,那么必须按照提供的未命名参数的顺序分配它们。 Kw1和 kw2在函数定义中提供了默认值,因此它们不是强制的,但是如果没有作为命名参数提供,它们将从其余提供的未命名参数中获取任何可用值。 剩余的任何未命名参数都提供给名为 args 的数组中的函数 任何在函数定义中没有相应参数名的命名参数都提供给名为 kwargs 的字典中的函数调用。

如果您希望在 Python2中实现这一点,那么我已经找到了在 这篇文章中使用装饰器解释的变通方法。

如果默认 kwarg 值没有定义为 严格来说,那么这个装饰器就会分配它们。

from functools import wraps


def force_kwargs(**defaultKwargs):
def decorator(f):
@wraps(f)
def g(*args, **kwargs):
new_args = {}
new_kwargs = defaultKwargs
varnames = f.__code__.co_varnames
new_kwargs.update(kwargs)
for k, v in defaultKwargs.items():
if k in varnames:
i = varnames.index(k)
new_args[(i, k)] = new_kwargs.pop(k)
# Insert new_args into the correct position of the args.
full_args = list(args)
for i, k in sorted(new_args.keys()):
if i <= len(full_args):
full_args.insert(i, new_args.pop((i, k)))
else:
break
# re-insert the value as a key-value pair
for (i, k), val in new_args.items():
new_kwargs[k] = val
return f(*tuple(full_args), **new_kwargs)
return g
return decorator

结果

@force_kwargs(c=7, z=10)
def f(a, b='B', c='C', d='D', *args, **kw):
return a, b, c, d, args, kw
#                                    a    b  c    d  args      kwargs
f('r')                           # 'r', 'B', 7, 'D',    (),       {'z': 10}
f(1, 2, 3, 4, 5)                 #   1,   2, 7,   3, (4,5),       {'z': 10}
f(1, 2, 3, b=0, c=9, f='F', z=5) #   1,   0, 9,   2,  (3,), {'f': 'F', 'z': 5}


变种

如果希望使用函数定义中写入的默认值,可以使用列出默认值的 f.func_defaults访问参数默认值。您必须在 f.__code__.varnames的末尾使用 zip将这些默认值与变量名匹配起来。

简明扼要:

Python 3.5或以上:

def foo(a, b=3, *args, **kwargs):
defaultKwargs = { 'c': 10, 'd': 12 }
kwargs = { **defaultKwargs, **kwargs }
  

print(a, b, args, kwargs)
  

# Do something else


foo(1) # 1 3 () {'c': 10, 'd': 12}
foo(1, d=5) # 1 3 () {'c': 10, 'd': 5}
foo(1, 2, 4, d=5) # 1 2 (4,) {'c': 10, 'd': 5}

注意: 你可以使用 在 < strong > Python2中

kwargs = merge_two_dicts(defaultKwargs, kwargs)

Python 3.5

kwargs = { **defaultKwargs, **kwargs }

Python 3.9

kwargs = defaultKwargs | kwargs  # NOTE: 3.9+ ONLY