繁体   English   中英

带有 *args 和 **kwargs 的默认参数

[英]Default arguments with *args and **kwargs

Python 2.x (我使用 2.7)中,这是使用带有*args**kwargs默认参数的正确方法吗?
我发现了一个与此主题相关的 SO 问题,但这是针对Python 3 的
使用 *args、**kwargs 和可选/默认参数调用 Python 函数

在那里,他们说这种方法有效:

def func(arg1, arg2, *args, opt_arg='def_val', **kwargs):
    #...

在 2.7 中,它会导致SyntaxError 有没有推荐的方法来定义这样的函数?
我以这种方式工作,但我想有一个更好的解决方案。

def func(arg1, arg2, *args, **kwargs):
    opt_arg ='def_val'
    if kwargs.__contains__('opt_arg'):
        opt_arg = kwargs['opt_arg']
    #...

只需将默认参数放在*args之前:

def foo(a, b=3, *args, **kwargs):

现在,如果将b作为关键字参数或第二个位置参数传递,则将显式设置b

例子:

foo(x) # a=x, b=3, args=(), kwargs={}
foo(x, y) # a=x, b=y, args=(), kwargs={}
foo(x, b=y) # a=x, b=y, args=(), kwargs={}
foo(x, y, z, k) # a=x, b=y, args=(z, k), kwargs={}
foo(x, c=y, d=k) # a=x, b=3, args=(), kwargs={'c': y, 'd': k}
foo(x, c=y, b=z, d=k) # a=x, b=z, args=(), kwargs={'c': y, 'd': k}

请注意,特别是foo(x, y, b=z)不起作用,因为在这种情况下b是按位置分配的。


此代码也适用于 Python 3。 在 Python 3 中将默认 arg 放在*args之后使其成为“仅关键字”参数,只能按名称指定,而不能按位置指定。 如果您想在 Python 2 中使用仅关键字参数,则可以使用 @mgilson 的解决方案

另一个问题中的语法仅是 python3.x 并指定仅关键字参数。 它不适用于 python2.x。

对于 python2.x,我会将它从 kwargs 中pop

def func(arg1, arg2, *args, **kwargs):
    opt_arg = kwargs.pop('opt_arg', 'def_val')

与@yaccob 类似的方法,但清晰简洁:

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    

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

你也可以使用这样的装饰器:

import functools
def default_kwargs(**defaultKwargs):
    def actual_decorator(fn):
        @functools.wraps(fn)
        def g(*args, **kwargs):
            defaultKwargs.update(kwargs)
            return fn(*args, **defaultKwargs)
        return g
    return actual_decorator

然后就这样做:

@default_kwargs(defaultVar1 = defaultValue 1, ...)
def foo(*args, **kwargs):
    # Anything in here

例如:

@default_kwargs(a=1)
def f(*args, **kwargs):
    print(kwargs['a']+ 1)

f() # Returns 2
f(3) # Returns 4

在尝试使其更通用和更紧凑的同时,非常接近您的解决方案方法,我建议考虑这样的事情:

>>> def func(arg1, arg2, *args, **kwargs):
...     kwargs_with_defaults = dict({'opt_arg': 'def_val', 'opt_arg2': 'default2'}, **kwargs)
...     #...
...     return arg1, arg2, args, kwargs_with_defaults

>>> func('a1', 'a2', 'a3', 'a5', x='foo', y='bar')
('a1', 'a2', ('a3', 'a5'), {'opt_arg2': 'default2', 'opt_arg': 'def_val', 'y': 'bar', 'x': 'foo'})

>>> func('a1', 'a2', 'a3', 'a5', opt_arg='explicit_value', x='foo', y='bar')
('a1', 'a2', ('a3', 'a5'), {'opt_arg2': 'default2', 'opt_arg': 'explicit_value', 'y': 'bar', 'x': 'foo'})

另一种处理 Python 2.x 的方法:

def foo(*args, **kwargs):
    if 'kwarg-name' not in kwargs.keys():
        kwargs['kwarg-name'] = 'kwarg-name-default-value'
    return bar(*args, **kwargs)

与@nneonneo 的回答不同,这会处理将任意*args传递给底层调用。

这个答案是Daniel Américo 建议的延伸。

如果没有严格定义,这个装饰器会分配默认的 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)
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',   (),         {}
f(1,2,3)                    #   1,   2, 7,   3,   (),         {}
f(1, 2, 3, b=3, c=9, f='F') #   1,   3, 9,   2, (3,), {'f': 'F'}

变体

如果您想使用函数定义中写入的默认值,您可以使用f.func_defaults访问参数默认值,其中列出了默认值。 您必须将它们与f.__code__.varnames的末尾进行zip ,以将这些默认值与变量名称相匹配。

暂无
暂无

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

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