Python 具有可选参数的dict初始化的最佳样式
我想做以下工作:Python 具有可选参数的dict初始化的最佳样式,python,keyword-argument,Python,Keyword Argument,我想做以下工作: 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,
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中记录它们,但即使这样也不会阻止传递不受支持的参数。防止这种情况发生会使我的函数同样冗长
我是否错过了一个明显的方法,可以快速、明确地完成这项工作?如何填写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)
或:
我对优雅和快速的方式感到困惑,但基于装饰师的方法至少可以隐藏血淋淋的细节:
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函数一样
你也可以这样做:
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)
但我反对。更令人费解的是,一开始没有显式的参数名,仍然会弄乱函数签名。或者以我个人的命名,这绝对是邪恶的;-) 使用会给你一些非常简洁的东西:
#!/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'}
如果要考虑变量和关键字参数,则需要修改此选项,但从中获得的
ArgInfo
元组包含附加信息。第二个示例中,什么是som“模糊”的?我认为这很简单——也许我会在第一行使用dict
构造函数(即pars=dict(name=name)
)。你到底想从解决方案中得到什么?如果没有说明这一点,这个问题就求助于一个主要基于观点的问题。@skyking:这个问题不太清楚,因为从函数实现来看,我对支持的参数没有任何线索。如果我将一个不受支持的参数传递给func
,它将毫无怨言地吞下它——这是我不想要的。allowed_args=set(('par1','par2','par3');allowed_args.issuperset(kwargs)
可能会成为装饰者@allowed_kwargs(…)
。不是太快,但可能更好。是的,我在考虑这个问题,但是pars
初始化行太长,因此必须将其拆分为几行(PEP8),使其同样冗长。在这种情况下,我的第一个实现看起来更好(更快?),你可以把它分成几行。例如,第一行有3个参数,第二行有3个参数,依此类推。我相应地更新了我的示例。但是pep8建议不要像第一个示例中那样进行一行if
测试,因此如果您遵循pep8,则第一个示例的长度至少会增加一倍。不过,这仍然无助于解决函数签名问题。@Blackcat不幸,不。你可以设计一个装饰包装器来收集关键字参数,并传递kwargs和显式kwargs dict。但这也会使你的函数签名不干净。太麻烦了;-)。不过,我可能不会接受这样的表现。
#!/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'}