繁体   English   中英

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

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

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

可以在 Python 3 中做到这一点。

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

*仅在您想指定仅关键字参数而不接受带有*args的可变数量的位置参数时使用。 你不使用两个* s。

引用语法,在Python 2 中,你有

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

而在Python 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 的字典中的函数调用。

简洁明了:

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

如果你想在 Python 2 中做到这一点,我在这篇文章中找到了一个使用装饰器解释的解决方法。

如果没有严格定义,这个装饰器会分配默认的 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 ,以将这些默认值与变量名称相匹配。

暂无
暂无

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

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