使用* args,** kwargs和optional / default参数调用Python函数

时间:2012-03-26 13:16:28

标签: python python-3.x syntax kwargs

在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打包成args或kwargs。但是,使用python 3,可以指定仅关键字参数:

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字典来模拟该行为。

4 个答案:

答案 0 :(得分:57)

可以在Python 3中执行此操作。

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

仅当您想要指定仅关键字参数接受*的可变数量的位置参数时,才使用裸*args。您不使用两个*

引用语法,在Python 2,你有

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

Python 3中,您有

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

其中包含*参数之后的其他参数的规定。

<强>更新

最新的Python 3文档here

答案 1 :(得分:6)

如果你想混合两者,请记住* 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的字典中提供给函数调用。

答案 2 :(得分:0)

如果您希望在Python 2中做到这一点,我发现使用装饰器在this post中介绍了一种解决方法。

如果未严格定义 ,则此修饰器将分配默认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}

Variant

如果要使用函数定义中编写的默认值,则可以使用f.func_defaults访问自变量默认值,其中列出了默认值。您必须在zip的末尾f.__code__.varnames处将这些默认值与变量名进行匹配。

答案 3 :(得分:0)

简洁明了:

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}

注意:您可以使用 在 Python 2

kwargs = merge_two_dicts(defaultKwargs, kwargs)

Python 3.5

kwargs = { **defaultKwargs, **kwargs }

Python 3.9

kwargs = defaultKwargs | kwargs  # NOTE: 3.9+ ONLY