简体   繁体   中英

Python. List to nested dictionary: strange behaviour

I'm a Python 3.6 user. I'm facing an apparent simple task: convert a list to a dict . I saw this helpful question for taking inspiration.

Let me explain my goal: starting from a list of objects like this:

class AObject():
def __init__(self, sp, sb, n):
    self.superset = sp
    self.subset   = sb
    self.number   = n

I want to have a dictionary of this type: d[superset][subset] = number .

My starting point is a simple list:

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))]

whose length is 4.

Now I create the dictionary in this way:

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

but

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

Where did the other two dictionary items go??

Same result using:

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

Meanwhile with a for loop:

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}})

My questions:

  • I think in same way is happening an update of the dictionary when I use dict comprehension keeping just one element for each superset as explained here . Am I right?

  • How can I build my nested dictionary in a pythonic way?

The problem you have is here:

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

anytime you get a new x.superset that is already in the dict to be built, it overrides the former one - its similar to

d2 = { k:v for k,v in [ (1,4),(1,7),(1,9)]} # d2 == {1:9,} - last value to key 1 survives

The fact that you box one layer more doesn't matter - if you provide multiple same keys in the dict-comp you get overwriting behaviour - not update-behaviour.

Your

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

is as pythonic as it gets.


The defaultdict-aproach is similar (but more effective) to:

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

The dict comp approach is similar to:

for x in o:
    d2[x.superset] = {x.subset:x.number}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM