【问题标题】:How to convert a dict to list of dicts in Python?如何将字典转换为 Python 中的字典列表?
【发布时间】:2019-02-22 07:16:09
【问题描述】:

我有以下问题:我想提供一个简单的函数,它遍历参数集列表并对所有参数集执行相同的操作。这个想法是将标量值或任何可迭代的值作为 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))

我已经检查过了,但没有成功: list of dicts to/from dict of lists

我的主要问题是:

  • 字符串是可迭代的
  • 我无法仅通过评估来检查生成器的长度

如果有任何想法,我将不胜感激!

【问题讨论】:

    标签: python python-3.x


    【解决方案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
    

    您可以将其用作起始块。代码 get 是一个字典,查找里面最长的列表并从中创建一组字典。您需要增强它以使用列表列表。如果你给出一个字符串,它将出现在任何列表中:

    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'}]
    

    如果列表太短,它将从输出字典中丢失。在断言“相等”时,您仍然必须处理排序,以防此算法确实以不同的顺序创建内部字典。

    【讨论】:

    • 谢谢,我需要支持所有类型的可迭代对象,尤其是 Tuple 和 Range 对象,所以我需要做一些工作。幸运的是,我现在不需要支持列表列表。
    【解决方案2】:

    要以通用方式解决问题,请考虑将所有 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 的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-12
      • 2015-07-23
      • 2019-02-10
      • 2016-01-10
      • 1970-01-01
      • 1970-01-01
      • 2011-07-11
      相关资源
      最近更新 更多