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

时间:2013-03-08 19:43:13

标签: python python-2.7

Python 2.x (我使用2.7)中,这是使用*args**kwargs的默认参数的正确方法吗? 我发现了一个与此主题相关的问题,但这是针对 Python 3
Calling a Python function with *args,**kwargs and optional / default arguments

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

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']
    #...

7 个答案:

答案 0 :(得分:64)

只需将默认参数放在*args

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

现在,如果将其作为关键字参数或第二个位置参数传递,将显式设置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中将 *args之后的默认arg 设置为“仅关键字”参数,可以按名称而不是按位置指定。如果你想在Python 2中使用仅限关键字的参数,你可以使用@ mgilson的解决方案。

答案 1 :(得分:50)

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

对于python2.x,我希望pop出于kwargs:

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

答案 2 :(得分:2)

您还可以使用这样的装饰器:

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

答案 3 :(得分:1)

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

>>> 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'})

答案 4 :(得分:1)

与@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

答案 5 :(得分:0)

使用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传递给底层调用。

答案 6 :(得分:0)

此答案是Daniel Américo suggested的扩展。

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

Variant

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