在python中初始化默认关键字args列表



如何定义关键字参数字典的初始内容?这是一个丑陋的解决方案:

def f(**kwargs):
ps = {'c': 2}
ps.update(kwargs)
print(str(ps))

预期行为:

f(a=1, b=2) => {'c': 2, 'a': 1, 'b': 2}
f(a=1, b=2, c=3) => {'c': 3, 'a': 1, 'b': 2}

然而,我想在以下方面做更多的事情:

def f(**kwargs = {'c': 2}):
print(str(kwargs))

甚至:

def f(c=2, **kwargs):
print(str(???))

想法?

首先要解决当前解决方案的问题:

def f(**kwargs):
ps = {'c': 2}
ps.update(kwargs)
print(str(ps))

这将创建一个新字典,然后必须花时间使用 kwargs 中的所有值更新它。 如果 kwargs 很大,那可能效率相当低下,而且正如你指出的那样有点丑陋。

显然你的第二个是无效的。

至于第三种选择,奥斯汀·黑斯廷斯已经给出了它的实现

如果您使用 kwargs 而不是关键字参数作为默认值,则可能是有原因的(或者至少应该有;例如,定义c而不明确要求a的接口,即使实现可能需要c的值,也可能不需要b)。

一个简单的实现将利用dict.setdefault来更新 kwargs 的值,当且仅当键尚不存在时:

def f(**kwargs):
kwargs.setdefault('c', 2)
print(str(kwargs))

现在,正如OP在前面的评论中提到的,默认值列表可能很长,在这种情况下,您可以循环设置默认值:

def f(**kwargs):
defaults = {
'c': 2,
...
}
for k, v in defaults.items():
kwargs.setdefault(k, v)
print(str(kwargs))

这里也有几个性能问题。首先,在每次调用函数时创建defaults字典文本。 这可以通过将默认值移到函数之外来改进,如下所示:

DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
for k, v in DEFAULTS.items():
kwargs.setdefault(k, v)
print(str(kwargs))

其次,在 Python 2 中,dict.items返回(键、值)对的副本,因此dict.iteritemsdict.viewitems允许您迭代内容,因此效率更高。 在Python 3中,'dict.items'是一个视图,所以没有问题。

DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
for k, v in DEFAULTS.iteritems():  # Python 2 optimization
kwargs.setdefault(k, v)
print(str(kwargs))

如果效率和兼容性都是问题,则可以使用six库来实现兼容性,如下所示:

from six import iteritems
DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
for k, v in iteritems(DEFAULTS):
kwargs.setdefault(k, v)
print(str(kwargs))

此外,在for循环的每次迭代中,都需要执行kwargssetdefault方法的查找。 如果您确实有大量的默认值,则微优化是将方法分配给变量以避免重复查找:

from six import iteritems
DEFAULTS = {
'c': 2,
...
}
def f(**kwargs):
setdefault = kwargs.setdefault
for k, v in iteritems(DEFAULTS):
setdefault(k, v)
print(str(kwargs))

最后,如果默认值的数量预计大于 kwarg 的数量,则使用 kwarg 更新默认值可能会更有效。 为此,不能使用全局默认值,否则它会在每次运行函数时更新默认值,因此需要将默认值移回函数中。这将给我们留下以下内容:

def f(**kwargs):
defaults = {
'c': 2,
...
}
defaults.update(kwargs)
print(str(defaults))

享受:D

在 Python 3.5+ 上的第一种方法的一个可能的变体是定义默认值并在一行上扩展提供的参数,这也允许您在同一行上替换kwargs,例如:

def f(**kwargs):
# Start with defaults, then expand kwargs which will overwrite defaults if
# it has them
kwargs = {'c': 2, **kwargs}
print(str(kwargs))

另一种方法(不会生成相同的字符串),该方法使用collections.ChainMap(3.3+) 创建行为方式相同的映射:

from collections import ChainMap
def f(**kwargs):
# Chain overrides over defaults
# {'c': 2} could be defined outside f to avoid recreating it each call
ps = ChainMap(kwargs, {'c': 2})
print(str(ps))

就像我说的,这不会产生相同的字符串输出,但与其他解决方案不同,随着传递的关键字参数数量的增加,它不会变得越来越昂贵(它根本不需要复制它们)。

你试过吗:

def f(c=2, **kwargs):
kwargs['c'] = c
print(kwargs)

更新

除此之外,您可以使用inspect来访问代码对象,并从中获取仅关键字的参数,甚至所有参数:

import inspect
def f(a,b,c=2,*,d=1,**kwargs):
code_obj = inspect.currentframe().f_code
nposargs = code_obj.co_argcount
nkwargs = code_obj.co_kwonlyargcount
localvars = locals()
kwargs.update({k:localvars[k] for k in code_obj.co_varnames[:nposargs+nkwargs]})
print(kwargs)
g=f
g('a', 'b')

最新更新