说明
我试图创建一个帮助函数,它多次调用另一个函数。对于helper函数,我希望将变量作为**kwargs传入,以便允许main函数确定每个参数的默认值。
传入的参数可以是可变长度的可迭代对象,并将被连接到多个字典中。下面是一个输入和解析表单的示例:
{'param1': ['arg1'], 'param2': ['arg1', 'arg2', 'arg3'], 'param3': ['arg1', 'arg2']}
#=>
[{'param1': 'arg1', 'param2': 'arg1', 'param3': 'arg1'}, {'param2': 'arg2', 'param3': 'arg2'}, {'param2': 'arg3'}]
python有没有内置的函数可以让你用这种方式扁平化一个字典?我想保留键值对,因为它们将在调用main函数时用作关键字参数。
我尝试了:
首先,我试图避免将**kwargs传递到主函数,方法是将参数转换为列表,然后将它们传递到itertools.zip_longest()
。
for data, param1, param2, param3 in itertools.zip_longest(external_data, argv1, argv2, argv3):
foo(data, param1, param2, param3) # Invoke main function
但是,这会强制使用None
或其他填充值,并隐藏main函数定义的默认值。
其次,我使用了嵌套列表解析来解析**kwargs,并创建了一个类似于我上面描述的字典列表。
foo = [{k: v[idx]
for k, v in kwargs.items() if idx < len(v) and v[idx] is not escape}
for idx in range(len(longest_argument_list))]
然而,这迫使我在解析**kwargs之前迭代所有的kwargs.values()
以获得最长参数列表的长度。
我在寻找什么
理想情况下,有一种更简单的方法可以使用内置函数将**kwargs扁平化到多个字典中。如果没有,可能有一个内置的比嵌套列表解析方法性能更好的方法。
允许某种形式的标记值来表示需要跳过特定函数调用的参数(例如:传入param1=['arg1', None, 'arg3']
以允许main的第二次调用使用param1
的默认值)。
展示预期行为的脚本:
import collections
import inspect
def invoked_function(param, param1=None, param2='', param3='.'):
"""This function only prints its own call, but it would
perform some actions using param and **kwargs"""
variables = inspect.currentframe().f_locals
function = inspect.currentframe().f_code.co_name
output = f'{function}(**{variables})'
print(output)
def helper_function(**kwargs):
external_data = ['target1', 'target2', 'target3', 'target4']
longest_argument_list = max(kwargs.values(), key=len)
escape = None
foo = [{k: v[idx]
for k, v in kwargs.items() if idx < len(v) and v[idx] is not escape}
for idx in range(len(longest_argument_list))]
foo = collections.deque(foo)
for target in external_data:
kwargs = foo.popleft() if foo else {}
invoked_function(target, **kwargs)
if __name__ == '__main__':
helper_function(param1=['arg1'],
param2=['arg1', 'arg2', 'arg3'],
param3=['arg1', 'arg2'])
上面的脚本按原样工作。
3条答案
按热度按时间628mspwn1#
我希望我没理解错你的问题。如果你想用可变参数调用
invoked_function()
,你可以使用自定义sentinel对象的zip_longest
:图纸:
svmlkihl2#
您可以使用此命令展平结构:
输出:
我使用了'None',因为您也希望使用默认值,但您可以使用任何“unaccepted”参数代替它。
h7appiyu3#
在对这个问题进行了更多的思考之后,我意识到可以通过将平坦化函数与辅助函数分离来改进原始程序结构。
使用Andrej Kesely's Answer作为如何使用
zip_longest
的灵感,我想出了这个解决方案:在非生成器形式中(用于timeit测试):
最明显的缺点是,
itertools.zip_longest
集合fillvalue
的kwargs不会被过滤掉。作为交换,这种实现比dict表达式构造更快。
list()
和dict()
是用C实现的,而dict和list表达式是用Python字节码实现的。以下是我使用timeit模块在系统上获得的一些数字:
实际上,这种差异非常小,除非您处理大量数据,否则不会产生太大的影响。我最终使用了生成器实现,因为它在我的项目中带来了更好的代码可重用性。