使用可选参数进行dict初始化的最佳样式

时间:2015-08-26 10:55:37

标签: python kwargs

我想做以下事情:

def func(name, par1=None, par2=None, par3=None, ...):
    pars = { 'name': name }
    if par1: pars['par1'] = par1
    if par2: pars['par2'] = par2
    if par3: pars['par3'] = par3
    ...
    do_something(pars)

这太冗长了。我当然可以这样做:

def func(name, **kwargs):
    pars = { 'name': name }
    pars.update(**kwargs)
    do_something(pars)

但这太模糊了:我不知道允许哪些参数。唯一的方法是在docstring中记录它们,但即使这样也不会阻止传递不受支持的参数。防止这种情况会使我的功能同样冗长。

我是否遗漏了一种明显的方法,明确地明确地执行此操作?

3 个答案:

答案 0 :(得分:2)

使用inspect可以为您提供非常简洁的内容:

#!/usr/bin/env python2.7
import inspect

def args_dict():
    outer_frame = inspect.currentframe().f_back
    info = inspect.getargvalues(outer_frame)
    return {
      k: info.locals[k] for k in info.args
      if info.locals[k] is not None # or modify for other falsey values
    }


def func(foo=None, bar=None, baz=None, qux='42'):
    args = args_dict()
    args['other'] = 'hello world'
    return args

print func(bar="BAR")
# prints: {'qux': '42', 'other': 'hello world', 'bar': 'BAR'}

如果您想要考虑变量和关键字参数,则需要修改此项,但是从getargvalues()获得的ArgInfo元组具有其他信息。

答案 1 :(得分:0)

如何填写dict,然后使用dict理解过滤掉无效值:

def func(name, par1=None, par2=None, par3=None, 
         par4=None, par5=None, par6=None, 
         ...):
    pars = {'par1': par1, 'par2': par2, 'par3': par3, 
            'par4': par4, 'par5': par5, 'par6': par6,
            ...}
    pars = {key, value for key, value in pars.items() if value}
    pars['name'] = name
    do_something(pars)

你也可以从dict中删除无效元素:

def func(name, par1=None, par2=None, par3=None, 
         par4=None, par5=None, par6=None, 
         ...):
    pars = {'par1': par1, 'par2': par2, 'par3': par3, 
            'par4': par4, 'par5': par5, 'par6': par6,
            ...}
    for key, value in pars.items():
        if not value:
            del pars[key]
    pars['name'] = name
    do_something(pars)

可以从一开始就放入name,但这会导致更详细的测试:

def func(name, par1=None, par2=None, par3=None, 
         par4=None, par5=None, par6=None, 
         ...):
    pars = {'name': name, 'par1': par1, 'par2': par2, 
            'par3': par3, 'par4': par4, 'par5': par5, 
            'par6': par6, ...}
    pars = {key, value for key, value in pars.items() if value or key == 'name'}
    do_something(pars)

或:

def func(name, par1=None, par2=None, par3=None, 
         par4=None, par5=None, par6=None, 
         ...):
    pars = {'name': name, 'par1': par1, 'par2': par2, 
            'par3': par3, 'par4': par4, 'par5': par5, 
            'par6': par6, ...}
    for key, value in pars.items():
        if not value and key != 'name':
            del pars[key]
    do_something(pars)

答案 2 :(得分:0)

我失去了优雅和快速的方式,但基于装饰的方法至少可以隐藏血腥的细节:

from functools import wraps

def allowed_kwargs(*args):
    allowed = frozenset(args)
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for kwarg in kwargs:
                if kwarg not in allowed:
                    raise TypeError("{} got an unexpected keyword argument '{}'".format(func.func_name, kwarg))
            return func(*args, **kwargs)
        return wrapper

    return decorator

用作

@allowed_kwargs('foo')    
def test(**kwargs):    
    print kwargs    

test(bar=1) 

这会引发TypeError,因为带有显式关键字参数的unwrapped函数会。{/ p>

您也可以这样做:

def collect_kwargs(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        forward_args = kwargs.copy()
        forward_args['kwargs_dict'] = kwargs
        return func(*args, **forward_args)
    return wrapper

    @collect_kwargs
    def func(par1=None, par2=None, ..., kwargs_dict=None):
        do_something(**kwargs_dict)

但我建议不要这样做。看起来更加模糊的是,首先没有明确的参数名称,它仍然使你的功能签名变得混乱。或者在我个人的命名法中,这是绝对邪恶的; - )。