如何在Python中将字典转换为字典列表?

3

我有以下问题:我想提供一个简单的函数,它可以迭代一组参数,并对所有集合执行相同的操作。这个想法是将标量值或任何可迭代对象作为 kwarg 传递给函数,然后生成每个 kwarg 集合,然后在其中调用操作。

def simple_function(**kwargs):
    list_of_kwargs = convert_kwargs(kwargs)
    return [my_action(**kwargs_set) for kwargs_set in list_of_kwargs]

所以,考虑到这一点,我觉得实现高效和通用的convert_kwargs很困难。一些需要满足的示例测试用例:

class ConvertParamsTestCase(unittest.TestCase):

    def test_convert_kwargs_no_list(self):
        kwargs = {'arg1': 1, 'arg2': "string", 'arg3': None}
        self.assertDictEqual(kwargs, convert_kwargs(kwargs))

    def test_convert_kwargs_all_lists(self):
        kwargs = {'arg1': [1, 2], 'arg2': ["string", "str"], 'arg3': [None, None]}
        expected = [{'arg1': 1, 'arg2': "string", 'arg3': None}, {'arg1': 2, 'arg2': "str", 'arg3': None}]
        self.assertListEqual(expected, convert_kwargs(kwargs))

    def test_convert_kwargs_one_is_string(self):
        kwargs = {'arg1': [1, 2], 'arg2': "string", 'arg3': [None, None]}
        expected = [{'arg1': 1, 'arg2': "string", 'arg3': None}, {'arg1': 2, 'arg2': "string", 'arg3': None}]
        self.assertListEqual(expected, convert_kwargs(kwargs))

    def test_convert_kwargs_one_is_iterator(self):
        kwargs = {'arg1': range(1, 3), 'arg2': "string", 'arg3': [None, None]}
        expected = [{'arg1': 1, 'arg2': "string", 'arg3': None}, {'arg1': 2, 'arg2': "string", 'arg3': None}]
        self.assertListEqual(expected, convert_kwargs(kwargs))

我已经检查过,但没有成功: 列表字典转换为字典列表 我的主要问题是:
  • 字符串是可迭代的
  • 我不能通过对生成器进行求值来检查其长度
如有任何想法,我将不胜感激!
2个回答

1

您可以检查字符串并对其执行不同的操作:

if isinstance(something,str): 
    do_thing_with_string(something) 
else: 
    do_thing_with_iterable(something)

你不能检查生成器的长度。
def gen():
    i = 0
    while True: 
        yield i 
        i+=1

endless = gen()         
for _ in range(20):        
    print(next(endless))  # works ... while len(endless) won't

你可以将此作为起始块。代码获取一个字典,查找其中最长的列表,并从中创建一组字典。您需要增强它以处理列表的列表。如果您提供一个字符串,它将出现在任何列表中:
def convert(paramdict):
    l = []
    # lists ordered by lenght, longest first, strings in the back
    params =  sorted(paramdict.items(), 
                     key=lambda x:len(x[1]) if isinstance(x[1],list) else -1,
                     reverse=True)

    # max number of inner dicts to create
    maxone = len(params[0][1]) if isinstance(params[0][1],list) else 1

    for n_th in range(maxone):
        l.append({})
        for k,value in params:
            if isinstance(value,list):
                if len(value) > n_th:
                    l[-1][k] = value[n_th]
            # add other things here: tuple, set, generator
            # it might be easiest to convert f.e. set/tuple to list
            # for generator you would need to takewhile() from it until
            # you got as much as you need
            else:
                l[-1][k] = value

    return l


print(convert( {'arg1': [1, 2],       'arg2': ["string", "str", "s"], 
                'arg3': [None, None], 'arg4': "some lengthy strings"} ) )

输出:

[{'arg2': 'string', 'arg1': 1, 'arg3': None, 'arg4': 'some lengthy strings'}, 
 {'arg2': 'str',    'arg1': 2, 'arg3': None, 'arg4': 'some lengthy strings'}, 
 {'arg2': 's',                               'arg4': 'some lengthy strings'}]

如果列表太短,则会在输出字典中缺失。当断言“相等性”时,您仍需要处理排序,以防此算法创建不同顺序的内部字典。

1
谢谢,我需要支持所有类型的可迭代对象,特别是Tuple和Range对象,所以我需要花点时间来处理它。幸运的是,我目前不需要支持列表的列表。 - Hodossy Szabolcs

1
为了以通用的方式解决问题,请考虑将所有argx值都转换为列表类型。
如果您定义了一个协议,其中所有参数都是值的列表:
kwargs = {
           'arg1': range(0,3), 'arg2': ["s1"]*3, 'arg3': [10, 20]
         }

提供:

[{'arg1': 0, 'arg2': 's1', 'arg3': 10},
 {'arg1': 1, 'arg2': 's1', 'arg3': 20},
 {'arg1': 2, 'arg2': 's1', 'arg3': None}]

这是一个解决方案:
kwargs = {'arg1': range(0, 3), 'arg2': ["s1"]*3, 'arg3': [10, 20]}

max_args = max([len(kwargs[key]) for key in kwargs])

list_of_kwargs = [{k: kwargs[k][index] if index < len(
    kwargs[k]) else None for k in kwargs} for index in range(max_args)]

请注意,对于传递相同的字符串值,这是一个 ["value"]*3 的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接