[英]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.