Nested defaultdict of defaultdict
For an arbitrary number of levels:
def rec_dd():
return defaultdict(rec_dd)
>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}
Of course you could also do this with a lambda, but I find lambdas to be less readable. In any case it would look like this:
rec_dd = lambda: defaultdict(rec_dd)
Nested `defaultdict of defaultdict of defaultdict` each with a backreference
Solution 1
Giving the same {BACKREF: node}
to defaultdict
:
from collections import defaultdict
BACKREF, END = 'BACKREF', '$'
words = ['hi', 'hello', 'hiya', 'hey']
tree = lambda: defaultdict(tree, {BACKREF: node})
node = None
root = tree()
for word in words:
node = root
for ch in word:
node = node[ch]
node[END] = None
The root
node has a backref None
, can be deleted if bothersome.
Solution 2
The above works fine if that code is the only code that creates tree nodes (seems likely to me, judging by the times I've built such trees myself). Otherwise you'd need to ensure that node
points to the correct parent node. If that's an issue, here's an alternative with a dict (not defaultdict) subclass that implements __missing__
to automatically create children with backrefs when needed:
BACKREF, END = 'BACKREF', '$'
words = ['hi', 'hello', 'hiya', 'hey']
class Tree(dict):
def __missing__(self, key):
child = self[key] = Tree({BACKREF: self})
return child
root = Tree()
for word in words:
node = root
for ch in word:
node = node[ch]
node[END] = None
Also doesn't give the root a backref, and being a dict, its string representations are far less cluttered than a defaultdict's and thus far more readable:
>>> import pprint
>>> pprint.pp(root)
{'h': {'BACKREF': <Recursion on Tree with id=2494556270320>,
'i': {'BACKREF': <Recursion on Tree with id=2494556270400>,
'$': None,
'y': {'BACKREF': <Recursion on Tree with id=2494556270480>,
'a': {'BACKREF': <Recursion on Tree with id=2494556340608>,
'$': None}}},
'e': {'BACKREF': <Recursion on Tree with id=2494556270400>,
'l': {'BACKREF': <Recursion on Tree with id=2494556340288>,
'l': {'BACKREF': <Recursion on Tree with id=2494556340368>,
'o': {'BACKREF': <Recursion on Tree with id=2494556340448>,
'$': None}}},
'y': {'BACKREF': <Recursion on Tree with id=2494556340288>,
'$': None}}}}
The defaultdict result for comparison:
>>> pprint.pp(root)
defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': None,
'h': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930855152>,
'i': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930855312>,
'$': None,
'y': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930912832>,
'a': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930913232>,
'$': None})})}),
'e': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930855312>,
'l': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930912912>,
'l': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930912992>,
'o': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930913072>,
'$': None})})}),
'y': defaultdict(<function <lambda> at 0x000001A13760BE50>,
{'BACKREF': <Recursion on defaultdict with id=1791930912912>,
'$': None})})})})
defaultdict of defaultdict?
Yes like this:
defaultdict(lambda: defaultdict(int))
The argument of a defaultdict
(in this case is lambda: defaultdict(int)
) will be called when you try to access a key that doesn't exist. The return value of it will be set as the new value of this key, which means in our case the value of d[Key_doesnt_exist]
will be defaultdict(int)
.
If you try to access a key from this last defaultdict i.e. d[Key_doesnt_exist][Key_doesnt_exist]
it will return 0, which is the return value of the argument of the last defaultdict i.e. int()
.
Nested Defaultdicts in Python
You didn’t nest any defaultdict
s, so do that:
nested_dict = defaultdict(lambda: defaultdict(list))
and
nested_dict[keyword][feature].append(pos)
How to default a nested defaultdict() to a list of specified length?
You can specify that you want a default value of a defaultdict
that has a default value of [0, 0, 0]
from collections import defaultdict
dic01 = defaultdict(lambda: defaultdict(lambda: [0, 0, 0]))
dic01['year2018']['jul_to_sep_temperature'][0] = 25
print(dic01)
prints
defaultdict(<function <lambda> at 0x7f4dc28ac598>, {'year2018': defaultdict(<function <lambda>.<locals>.<lambda> at 0x7f4dc28ac510>, {'jul_to_sep_temperature': [25, 0, 0]})})
Which you can treat as a regular nested dictionary
python3 additional problem with nested default dict
First of all, we need to understand why this is happening and what are we going to sacrifice by fixing this error:
TypeError: unsupported operand type(s) for +=: 'collections.defaultdict' and 'int'
This error tells you that x['a']['b']['c']['e'] += 2
is adding two to a default dictionary. This is obviously happening because the defaultdict sets all new elements to defaultdict. This would allow you to do something like:
x['a']['b']['c']['e']['another_one']
That wouldn't be a problem because just like you've seen from your error, the element at ['e']
is a default dictionary allowing you to further index it with something like ['another_one']
. Now we can change your code to make the dictionary have integers at level four, but you would be sacrificing the ability to further index the dictionary to levels beyond 4:
Solution 1 (?)
from functools import partial
def rec_dd(depth=0):
if depth == 4:
return 0
return defaultdict(partial(rec_dd, depth + 1))
The function above returns a default dictionary until you reach level 4 at which you want to have integers instead.
Now let's take a look at the modified function above, it stops at depth 4 resulting in the following behavior:
x['a']['b']['g'] = 2
Will work without any problems, because you're setting the dictionary at depth 3 to an int.
x['a']['b']['h'] + 3
Will not work because everything up to depth 4 is dictionary and you're trying to add an int to a dictionary.
x['a']['b']['c']['d'] = 2
x['a']['b']['c']['e'] += 2
Both of those options will work because the level 4 now contains ints and not dictionaries. HOWEVER:
x['a']['b']['c']['e']['f'] = 2
You sacrifice the ability to index past level 4 because level 4 now has ints and ints are not subscriptable.
This solution would totally fix the example that you have provided but might not be the optimal solution if you want customizable depth. Luckily for you, there are already a lot of very high quality posts showing how to implement a nested dictionary where you can add any behavior to your nested container:
What is the best way to implement nested dictionaries?
I recommend reading through that first if you want further functionality.
defaultdict of defaultdict of int
Try this:
from collections import defaultdict
m = defaultdict(lambda: defaultdict(int))
m['a']['b'] += 1
Note if you want more depth, you can still use a recursive approach:
def ddict(some_type, depth=0):
if depth == 0:
return defaultdict(some_type)
else:
return defaultdict(lambda: ddict(some_type, depth-1))
m = ddict(int, depth=2)
m['a']['b']['c'] += 1
Python Defaultdict with defined dictionary as default value shares the same dictionary between keys
To fix this, you simply need to move your counter_format
dictionary construction into the lambda, so that a new counter_format
dictionary is created each time you try to access a missing value in your innermost defaultdict
from collections import defaultdict
userDict = defaultdict(lambda:
defaultdict(lambda: {"correct": 0, "incorrect": 0})
)
userDict['a'][1]['correct']+=1
userDict['b'][1]['correct']+=4
userDict['b'][2]['correct']+=1
print(userdict)
defaultdict(<function <lambda> at 0x7f0f6cb38550>,
{'a': defaultdict(<function <lambda>.<locals>.<lambda> at 0x7f0f679424c0>,
{1: {'correct': 1, 'incorrect': 0}}),
'b': defaultdict(<function <lambda>.<locals>.<lambda> at 0x7f0f4caaa430>,
{1: {'correct': 4, 'incorrect': 0},
2: {'correct': 1, 'incorrect': 0}})})
Related Topics
Change Parent Shell's Environment from a Subprocess
Fastest Way to Download 3 Million Objects from a S3 Bucket
How Is Python's List Implemented
How to Print the Full Numpy Array, Without Truncation
Removing Duplicates from a List of Lists
Convert Pandas Column Containing Nans to Dtype 'Int'
Matplotlib (Pyplot) Savefig Outputs Blank Image
How to Use Python Requests to Fake a Browser Visit A.K.A and Generate User Agent
Creating a Range of Dates in Python
No Such File or Directory "Limits.H" When Installing Pillow on Alpine Linux
Visibility of Global Variables in Imported Modules
Create Multiple Dataframes in Loop
Animated Sprite from Few Images
What's the Correct Way to Convert Bytes to a Hex String in Python 3