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


84

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

仍然是最简洁的解释,我整个运行:saltycrane.com/blog/2008/01/...
verbsintransit

4
永远不要显式调用__contains__。始终使用in'opt_arg' in kwargs。(更好的是:kwargs.get('opt_arg', 'def_val')如mgilson的回答所示)。
nneonneo

Answers:


80

只需将默认参数放在*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的解决方案。


1
是的,这是由于的含糊不清*args。如果需要仅关键字的参数,则方法是正确的。
nneonneo

刚刚在上面的评论中得到了答案。(如果我想用不同的b调用该函数,并且还要添加* args怎么办?)

无论如何,在询问之前,我尝试了此解决方案,但我也发现只有在定义opt_arg之后仅使用kwargs时,它才有效。

@nneonneo:我得到了您的示例,但是这种方式仍然不能使我们自由地同时指定默认参数和* args,就像Python 3.x允许的那样,如链接中所述;可以?

5
您不能只留下默认参数,同时还要填写*args,不。这就是为什么功能被添加到Python 3,通常情况下,在Python 2默认参数被指定为像0或明显的东西None,使他们可以明确地传递英寸
nneonneo

55

另一个问题的语法仅是python3.x,并指定仅关键字参数。在python2.x上不起作用。

对于python2.x,我不会pop出于夸夸其谈:

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

它还在默认参数之前指定* args。

4

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

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

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

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的答案不同,此方法可将任意传递给基础调用。


1
如果您使用有效的代码,则这会更清楚,例如,'opt_arg'代替< kwarg-name >'def_val'代替< kwarg-name-default-value >
wjandrea

0

该答案是DanielAmé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变量名匹配这些默认值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.