92

Python 2.x(我使用 2.7)中,将默认参数与*args和一起使用是正确的方法**kwargs吗?
我发现了一个与此主题相关的 SO 问题,但这是针对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']
    #...
4

7 回答 7

99

只需将默认参数放在*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中将默认 arg 放在后面 *args使其成为只能按名称而不是位置指定的“仅关键字”参数。如果您想要 Python 2 中的仅关键字参数,可以使用 @mgilson 的解决方案

于 2013-03-08T19:45:36.450 回答
60

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

对于python2.x,我会pop用kwargs:

def func(arg1, arg2, *args, **kwargs):
    opt_arg = kwargs.pop('opt_arg', 'def_val')
于 2013-03-08T19:45:23.387 回答
10

与@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
于 2020-11-20T08:53:31.110 回答
5

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

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
于 2018-04-30T20:31:23.253 回答
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'})
于 2018-02-10T02:24:23.283 回答
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)

*args与@nneonneo 的答案不同,这可以将任意传递给底层调用。

于 2016-02-08T17:38:03.910 回答
0

这个答案是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列出默认值的 访问参数默认值。您必须在zip它们的末尾f.__code__.varnames加上变量名来匹配这些默认值。

于 2020-08-18T21:54:17.837 回答