Python. 列表转嵌套字典:奇怪的行为

3
我是一名Python 3.6用户。我面临一个明显简单的任务:将一个列表转换为字典。我看到了这个有用的问题以获取灵感。
让我解释一下我的目标:从这样的对象列表开始:
class AObject():
def __init__(self, sp, sb, n):
    self.superset = sp
    self.subset   = sb
    self.number   = n

我希望有这样一种字典:d[superset][subset] = number
我的起点是一个简单的列表:
s = set((('a','a1'),('a','a2'),('b','b1'),('b','b2'))) #set of tuples
o = [AObject(t[0], t[1], n) for t, n in zip(s, range(0,4))]

长度为4。

现在我以这种方式创建字典:

d = {x.superset: {x.subset : x.number} for x in o}

但是
d
Out[5]: {'a': {'a1': 1}, 'b': {'b1': 3}}

其他两个字典项去哪了?

使用相同的结果:

d1 = dict(map(lambda x: (x.superset, {x.subset : x.number}), o))

同时,使用for循环:

from collections import defaultdict
d2 = defaultdict(dict)
for x in o:
    d2[x.superset][x.subset] = x.number

d2
defaultdict(dict, {'a': {'a1': 1, 'a2': 0}, 'b': {'b1': 3, 'b2': 2}})

我的问题:

  • 我认为当我使用字典推导式仅保留每个superset的一个元素时,字典也会更新,就像这里所解释的一样。我是正确的吗?

  • 如何以Pythonic的方式构建我的嵌套字典?


2
for x in o: ... 是 Pythonic 的写法。 - Patrick Artner
显式的for循环是有效的。但是有没有更紧凑的方法来创建嵌套字典呢? - Marco
1个回答

3
你遇到的问题在这里:

d = {x.superset: {x.subset : x.number} for x in o}
任何时候,如果你得到一个已经存在于要构建的字典中的新的 x.superset,它会覆盖原来的那个 - 这类似于。
d2 = { k:v for k,v in [ (1,4),(1,7),(1,9)]} # d2 == {1:9,} - last value to key 1 survives

如果在字典推导式中提供多个相同的键,则会发生覆盖行为,而不是更新行为。因此,增加一层嵌套并不影响结果。请注意保持 HTML 标签不变。
“Your” 之后缺少需要翻译的内容,请提供完整的文本。
from collections import defaultdict
d2 = defaultdict(dict)
for x in o:
    d2[x.superset][x.subset] = x.number

这是最符合Python风格的代码。


defaultdict方法的实现与以下方法类似(但更为高效):

d2 = {}
for x in o:
    k = d2.setdefault(x.superset,{})
    k[x.subset] = x.number

# {'a': {'a1': 1, 'a2': 0}, 'b': {'b1': 3, 'b2': 2}}

字典推导式的方法类似于:
for x in o:
    d2[x.superset] = {x.subset:x.number}

# {'a': {'a1': 1}, 'b': {'b1': 3}}

for 和字典推导式有什么不同呢?我的意思是:为什么后者会覆盖前者?我以为字典推导式是复制显式的 for 循环的一种简洁方式,但我可能漏掉了什么... - Marco
1
@Marco,区别在于使用defaultdict - 列表推导式使用“普通”字典,而defaultdict处理_如果此键已存在于字典中,则将其添加到指定键的值中,使用指定为defaultelement的任何内容,否则创建一个空的defaultelement并使用它来添加到_。 - Patrick Artner

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