将Python中的列表列表转换为字典字典

21

我正在尝试将一个列表套列表的数据结构转换为一个字典套字典。

该列表定义如下:

l = [
  ['PP','Ear-rings', 'Holesovice', 2000],
  ['PP','Skirts', 'Holesovice', 1000],
  ['PP','Dresses', 'E-shop', 1500],
  ['BM','Butterfly', 'Holesovice', 1600]
]

我的目标是将字典结构设计如下:

#{'PP' : {'Holesovice' : {'Ear-rings' : 2000, 'Skirts' : 1000},
#         'E-shop' : {'Dresses' : 1500}},
# 'BM' : {'Holesovice' : {'Butterfly' : 1600}}
#}

这段代码无法返回预期输出:

labels_d = {}
items_d = {}
shops_d = {}

for index, row in enumerate(l):
  items_d[row[1]] = row[3]
  shops_d[row[2]] = items_d
  labels_d[row[0]] = shops_d

print(labels_d)

我发现了一些关于将列表转换为字典的帖子,分别在这里这里,但是我没有按照我想要的方式使其工作。 有没有什么“干净”的方法来实现上面发布的结构?


如果你发现了一些帖子,那么肯定你尝试过一些东西,即使它失败了? - Mad Physicist
https://stackoverflow.com/q/43234439/2988730 - Mad Physicist
5个回答

27

使用dict.setdefault(key, {})是一种很好的方法来创建固定深度的嵌套字典。

l = [
  ['PP','Ear-rings', 'Holesovice', 2000],
  ['PP','Skirts', 'Holesovice', 1000],
  ['PP','Dresses', 'E-shop', 1500],
  ['BM','Butterfly', 'Holesovice', 1600]
]

d = {}

for tag, item, source, qty in l:
    d.setdefault(tag, {}).setdefault(source, {})[item] = qty 

输出

{'BM': {'Holesovice': {'Butterfly': 1600}},
 'PP': {'E-shop': {'Dresses': 1500},
        'Holesovice': {'Ear-rings': 2000, 'Skirts': 1000}}}

泛化

通过构建一个嵌套字典类,放弃对固定深度的要求,可以使上述解决方案更加通用。

class NestedDict(dict):
    def __getitem__(self, item):
        if item not in self:
            self[item] = NestedDict()
        return super().__getitem__(item)

d = NestedDict()

for tag, item, source, qty in l:
    d[tag][source][item] = qty 

同时注意,类的方法只有在键不存在时才会创建对象,而setdefault方法在每次访问时都会创建一个空的dict


4
每当调用setdefault时创建一个新对象,虽然这是一个非常好的解决方案,但它确实让我感到不爽。 - Mad Physicist
@OlivierMelançon他们的意思是,无论item是否已经有值,都会调用NestedDict(),这有点不太优雅。 - Patrick Haugh
1
@MadPhysicist 这个问题可以用一般的方法解决。 - Olivier Melançon
1
我知道解决方案,也相信你也知道。我喜欢你最初的答案因为它简单易懂。我的评论只是对setdefault的一般不满。如果有另一种方法接受可调用对象而不是预先构造实例的话,那将会很好。 - Mad Physicist
@MadPhysicist 我同意,我认为将setdefault设置为可调用函数会更好。 - Olivier Melançon
1
我以前从未在Python讨论主题上发布过任何帖子。也许现在是开始的好时机。我先看看是否能想出一个简单的实现。 - Mad Physicist

17

您可以使用无限嵌套的 defaultdict 技巧:

from collections import defaultdict

def nested_dict():
    return defaultdict(nested_dict)

nd = nested_dict()
for a, b, c, d in l:
    nd[a][c][b] = d

我也喜欢这个。如果您将嵌套字典(nested_dict)改为类而不是工厂模式,它会更加整洁。 - Olivier Melançon
1
@OlivierMelançon 当然,在类中以如此整洁的方式使用递归逻辑是一项挑战。毕竟,传递给defaultdict构造函数的参数是默认的工厂;-) - user2390182
1
@schwobaseggl,这其实很简单,因为工厂被存储为实例参数。class NestedDict(defaultdict): def __init__(self): self.default_factory = NestedDict - Olivier Melançon
@OlivierMelançon 确实,我也喜欢这个。它肯定提供了额外的见解。 - user2390182
1
这个答案当然是好的。但我认为需要注意这个和我的非常相似但更精确的答案之间的区别。当你检查一个键 nd['key1']['key2']['key3'] 并且它不存在时,你会得到一个空字典 {},而在我的解决方案中,你会得到 0。这可能是相关的,因为从概念上讲,你可能不会期望 {} - jpp
@jpp 有一个有效的观点需要在处理这种数据结构时考虑。与您的方法相比,这种方法的主要优点是能够处理任意(未知)深度的嵌套。 - user2390182

6
您可以使用collections.defaultdict并进行迭代。在这种情况下,您可以精确定义一个嵌套字典以反映您的数据结构。
from collections import defaultdict

L = [['PP','Ear-rings', 'Holesovice', 2000],
     ['PP','Skirts', 'Holesovice', 1000],
     ['PP','Dresses', 'E-shop', 1500],
     ['BM','Butterfly', 'Holesovice', 1600]]

d = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))

for code, item, shop, value in L:
    d[code][shop][item] = value

结果

defaultdict({'BM': defaultdict({'Holesovice': defaultdict(int, {'Butterfly': 1600})}),
             'PP': defaultdict({'E-shop': defaultdict(int, {'Dresses': 1500}),
                                'Holesovice': defaultdict(int,
                                {'Ear-rings': 2000, 'Skirts': 1000})})})

1
我花了一些时间才注意到,将字典底部返回0既微妙又整洁。 - Olivier Melançon

0
def toNested1(l):
    def addKeyDict(map,key):    
        if key not in map:
            item = map[key] = {}
            return item            
        return map[key]

    zz = {}
    for a0,a1,a2,a3 in l :
        addKeyDict( addKeyDict( zz, a0) , a2 )[a1] = a3
    return zz

0

这里介绍一种非常简单的方法来组成一个新字典:

如果列表中每一行的项目不在字典的相应深度中,则只需将键值对添加/附加到字典中。

代码:

list = [
    ['PP','Ear-rings', 'Holesovice', 2000],
    ['PP','Skirts', 'Holesovice', 1000],
    ['PP','Dresses', 'E-shop', 1500],
    ['BM','Butterfly', 'Holesovice', 1600]
]

dicta = {}
for row in list:
    if row[0] not in dicta.keys():
        dicta[row[0]] = {row[2]:{row[1]:row[3]}}
        continue
    if row[2] not in dicta[row[0]].keys():
        dicta[row[0]][row[2]] = {row[1]:row[3]}
        continue
    if row[1] not in dicta[row[0]][row[2]].keys():
        dicta[row[0]][row[2]][row[1]] = row[3]

print(dicta)

输出:

{'BM': {'Holesovice': {'Butterfly': 1600}},
 'PP': {'E-shop': {'Dresses': 1500},
        'Holesovice': {'Ear-rings': 2000, 'Skirts': 1000}}}

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