'How to nest both lists and sets in dict in python3.x? [closed]

I recently learned Python 3 and tried to define some complex data structures. Can someone help me with how to implement it?

import random
from collections import defaultdict
from pprint import pprint

d1 = defaultdict(dict)

d1['131']['all'] = [('1', 'a'), ('2', 'b'), ('3', 'c')]
d1['131']['uniq'] = ('a', 'b', 'c')

pprint(d1)

# var = {
#     'key': {
#         'all': [(),(),()],
#         'uniq': ()
#     }
# }

d2 = ???

num = [i for i in range(1, 27)]
ch = [chr(i) for i in range(97, 123)]

for i in num:
    d2[i]['all'].append((random.choice(num), random.choice(ch)))
    d2[i]['uniq'].add(random.choice(ch))

pprint(d2)


Solution 1:[1]

TL;DR

You can pass defaultdict a custom initializer that creates the structure you need. Scroll to my second answer for details.

My first answer: the manual approach

Note While my second answer might be more useful, I actually prefer the more manual approach to initializing the structure presented in this first answer, so I'm going to keep it here as an alternative solution.

There is no "list or set" data structure in Python. I think what you need to do is manage some of the logic manually yourself, deferring only part of the logic to defaultdict.

d2 = defaultdict(dict)

num = [i for i in range(1, 27)]
ch = [chr(i) for i in range(97, 123)]

for i in num:
    d2[i]['all'] = [(random.choice(num), random.choice(ch))]
    d2[i]['uniq'] = {random.choice(ch)

pprint(d2)

Later, if you need to modify d2[i]['all'] for a value where you don't know yet if it's been initialized, you could use a test like:

if 'all' in d2[i]:
    d2[i]['all'].append(stuff)
else:
    d2[i]['all'] = [stuff]

or, since you initialize 'all' and 'uniq' as a pair, initialize them both on a condition like if i in d2:.

My second answer: give defaultdict a custom initializer

You can give defaultdict a function that initializes the sub-structure the way you want. You don't have to use an existing or built-in constructor. Since your default entry seems be a dict with 'all' mapping to a list, and 'uniq' mapping to a set, create a function that returns such a dict and pass it to defaultdict:

def my_initializer():
    return {'all': list(), 'uniq': set()}

d3 = defaultdict(my_initializer)
for i in num:
    d3[i]['all'].append((random.choice(num), random.choice(ch)))
    d3[i]['uniq'].add(random.choice(ch))

pprint(d3)

Lambda expressions: more concise, but more cryptic

If you want to make the code more concise, at the cost of making it more cryptic, you can use a lambda expression to do the same thing:

d3 = defaultdict(lambda: {'all': list(), 'uniq': set()})

Here I am creating an anonymous function with no argument that returns the empty entry you want, just like my_initializer() did. And I pass that anonymous function to defaultdict as the thing to use to create new entries.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1