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

时间:2017-04-04 01:03:51

标签: python

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

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(???))

想法?

3 个答案:

答案 0 :(得分:3)

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

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

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

显然你的第二个不是有效的。

至于第三种选择,Austin Hastings

已经给出了实施方案

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

当且仅当密钥不存在时,一个简单的实现将利用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 dict文字。这可以通过在函数之外移动默认值来改进:

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循环的每次迭代中,都需要执行setdefault kwargs方法的查找。如果您确实拥有非常多的默认值,则微优化是将方法分配给变量以避免重复查找:

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

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

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

享受:D

答案 1 :(得分:1)

您在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))

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

答案 2 :(得分:0)

你试过了吗?

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

<强>更新

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

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