简体   繁体   English

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

[英]Calling a Python function with *args,**kwargs and optional / default arguments

In python, I can define a function as follows:在python中,我可以定义一个函数如下:

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

In this case, i can call func as:在这种情况下,我可以将 func 称为:

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

I can also define a function as:我还可以将函数定义为:

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

which can be called as这可以称为

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

Finally, I can combine the two forms最后,我可以将两种形式结合起来

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

But, what does not work is calling:但是,不起作用的是调用:

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

My original thought was that this was probably because a function我原来的想法是,这可能是因为一个函数

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

can be called as可以称为

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

So this would introduce some ambiguity as to whether 3 should be packed into args or kwargs.所以这会给 3 应该被打包到 args 还是 kwargs 中引入一些歧义。 However, with python 3, there is the ability to specify keyword only arguments:但是,使用 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)
   ...

With this, it seems that there is no syntactic ambiguity with:有了这个,似乎没有语法歧义:

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

However, this still brings up a syntax error (tested with Python3.2).但是,这仍然会带来语法错误(使用 Python3.2 测试)。 Is there a reason for this that I am missing?我失踪有什么原因吗? And, is there a way to get the behavior I described above (Having *args with default arguments) -- I know I can simulate that behavior by manipulating the kwargs dictionary inside the function.并且,有没有办法获得我上面描述的行为(具有带有默认参数的 *args)——我知道我可以通过操纵函数内部的 kwargs 字典来模拟这种行为。

You can do that in Python 3.可以在 Python 3 中做到这一点。

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

The bare * is only used when you want to specify keyword only arguments without accepting a variable number of positional arguments with *args .*仅在您想指定仅关键字参数而不接受带有*args的可变数量的位置参数时使用。 You don't use two * s.你不使用两个* s。

To quote from the grammar, in Python 2 , you have引用语法,在Python 2 中,你有

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

while in Python 3 , you have而在Python 3 中,你有

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

which includes a provision for additional parameters after the * parameter.其中包括在*参数之后提供附加参数的规定。

UPDATE:更新:

Latest Python 3 documentation here .最新的 Python 3 文档在这里

If you want to do a mixture of both remember that *args and **kwargs must be the last parameters specified.如果您想混合使用这两者,请记住 *args 和 **kwargs 必须是指定的最后一个参数。

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

The comments seem to be based on mixing up how a function definition is constructed compared to how the arguments provided are assigned back to the parameters specified in the definition.这些评论似乎是基于将函数定义的构造方式与提供的参数如何分配回定义中指定的参数的方式相混淆。

This is the definition of this function which has 6 parameters.这是这个函数的定义,它有 6 个参数。 It is called by passing named and unnamed arguments to it in a function call.它是通过在函数调用中向它传递命名和未命名参数来调用的。

For this example... When an argument is named when calling the function it can be provided out of order.对于此示例...当调用函数时命名参数时,它可以乱序提供。 arg1 and arg2 are mandatory parameters and if not passed to the function as named arguments, then they must be assigned in order from the provided unnamed arguments. arg1 和 arg2 是强制参数,如果不作为命名参数传递给函数,则必须从提供的未命名参数开始按顺序分配它们。 kw1 and kw2 have default values provided in the function definition so they are not mandatory, but if not provided for as named arguments they will take any available values from the remaining provided unnamed arguments. kw1 和 kw2 在函数定义中提供了默认值,因此它们不是强制性的,但如果没有作为命名参数提供,它们将从其余提供的未命名参数中获取任何可用值。 Any unnamed arguments left over are provided to the function in an array called args Any named arguments that do not have a corresponding parameter name in the function definition are provided to the function call in a dictionary called kwargs.剩下的任何未命名参数都提供给名为 args 的数组中的函数。任何在函数定义中没有相应参数名称的命名参数都提供给名为 kwargs 的字典中的函数调用。

Clear and concise:简洁明了:

In Python 3.5 or greater: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}

Note: you can use In Python 2注意:您可以在Python 2 中使用

kwargs = merge_two_dicts(defaultKwargs, kwargs)

In Python 3.5Python 3.5 中

kwargs = { **defaultKwargs, **kwargs }

In Python 3.9Python 3.9 中

kwargs = defaultKwargs | kwargs  # NOTE: 3.9+ ONLY

If you are looking to do that in Python 2, I have found a workaround explained in this post , using a decorator.如果你想在 Python 2 中做到这一点,我在这篇文章中找到了一个使用装饰器解释的解决方法。

This decorator assigns default kwarg values if they are not strictly defined.如果没有严格定义,这个装饰器会分配默认的 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

Result结果

@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变体

If you want to use the default values as written in the function definition, you could access the argument default values using f.func_defaults , which lists the default values.如果要使用函数定义中写入的默认值,可以使用f.func_defaults访问参数默认值,其中列出了默认值。 You would have to zip them with the end of the f.__code__.varnames to match these default values with the variable names.您必须将它们与f.__code__.varnames的末尾进行zip ,以将这些默认值与变量名称相匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM