python 带有 *args和**kwargs的缺省参数

idfiyjo8  于 2022-12-02  发布在  Python
关注(0)|答案(7)|浏览(116)

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):
    #...

在Python 2.7中,它的结果是SyntaxError。有什么推荐的方法来定义这样的函数吗?
我是这么想的,但我想还有更好的解决办法。

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

oyt4ldly1#

只需将默认参数放在*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之后,使它成为一个“keyword-only”参数,它 * 只能 * 由名称指定,而不是由位置指定。如果你想要Python 2中的keyword-only参数,你可以使用@mgilson的solution

lymgl2op

lymgl2op2#

另一个问题中的语法是python3.x only,并且只指定了关键字参数,它在python2.x上不起作用。
对于python2.x,我会用kwargs来pop它:

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

kx7yvsdv3#

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

注意:您可以使用InPython 2

kwargs = merge_two_dicts(defaultKwargs, kwargs)

Python 3.5中

kwargs = { **defaultKwargs, **kwargs }

Python 3.9中

kwargs = defaultKwargs | kwargs  # NOTE: 3.9+ ONLY
ygya80vv

ygya80vv4#

您也可以使用如下的装饰器:

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
tyg4sfes

tyg4sfes5#

在尽量使解决方案更通用、更紧凑的同时,坚持使用您的解决方案方法,我建议您考虑以下做法:

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

ppcbkaq56#

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

06odsfpq

06odsfpq7#

这个答案是什么Daniel Américo suggested的延伸。
如果没有 * 严格 * 定义kwarg值,这个装饰器会分配默认的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,以便将这些默认值与变量名相匹配。

相关问题