Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a specific data structure as the default_factory for a defaultdict?

I am currently using a defaultdict of Counter to uniquely count several unpredictable values for unpredictable keys:

from collections import defaultdict, Counter

d = defaultdict(Counter)
d['x']['b'] += 1
d['x']['c'] += 1
print(d)

This gives me the expected result:

defaultdict(<class 'collections.Counter'>, {'x': Counter({'c': 1, 'b': 1})})

I now need to expand the structure of the values in the defaultdict and make it a dict with two keys: the previous Counter and an str:

mystruct = {
    'counter': collections.Counter(),
    'name': ''
}

Is it possible to use a specific data structure (like the above) as the default_factory in defaultdict? The expected result would be that for each nonexistent key in the defaultdict, a new key and value initialized with the structure above would be created.

like image 805
WoJ Avatar asked Jul 30 '15 12:07

WoJ


People also ask

How does Defaultdict work Defaultdict stores a copy of a dictionary?

Defaultdict is a sub-class of the dictionary class that returns a dictionary-like object. The functionality of both dictionaries and defaultdict are almost same except for the fact that defaultdict never raises a KeyError. It provides a default value for the key that does not exists.

How do you declare a Defaultdict?

A defaultdict can be created by giving its declaration an argument that can have three values; list, set or int. According to the specified data type, the dictionary is created and when any key, that does not exist in the defaultdict is added or accessed, it is assigned a default value as opposed to giving a KeyError .

How does Defaultdict work Defaultdict will automatically?

The Python defaultdict type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, then defaultdict will automatically create the key and generate a default value for it. This makes defaultdict a valuable option for handling missing keys in dictionaries.


2 Answers

You just need to define your default_factory as a function that returns the dictionary you want to default to:

from collections import defaultdict, Counter
d = defaultdict(lambda: {'counter': Counter(), 'name': ''})
d['x']['counter']['b'] += 1
d['x']['counter']['c'] += 1
print(d)

If you are not familiar with lambdas, this is the same thing as:

def my_factory():
    aDict = {'counter': Counter(), 'name':''}
    return aDict
d = defaultdict(my_factory)
like image 108
drootang Avatar answered Nov 12 '22 08:11

drootang


An alternate solution to drootang's answer is to use a custom class:

from collections import defaultdict, Counter

class NamedCounter:
    def __init__(self, name = '', counter = None):
        self.counter = counter if counter else Counter()
        self.name = name

    def __repr__(self):
        return 'NamedCounter(name={}, counter={})'.format(
                repr(self.name), repr(self.counter))

d = defaultdict(NamedCounter)
d['x'].counter['b'] += 1
d['x'].counter['b'] += 1
d['x'].name = 'X counter'
print(d)

defaultdict(<class __main__.NamedCounter at 0x19de808>, {'x': NamedCounter(name='X counter', counter=Counter({'b': 2}))})

Alternatively, you can extend Counter to incorporate the name into the counter itself:

from collections import defaultdict, Counter

class NamedCounter(Counter):
    def __init__(self, name = '', dict = None):
        super(Counter, self).__init__(dict if dict else {})
        self.name = name

    def __repr__(self):
        return 'NamedCounter(name={}, dict={})'.format(
                repr(self.name), super(Counter, self).__repr__())

d = defaultdict(NamedCounter)
d['x']['b'] += 1
d['x']['b'] += 1
d['x'].name = 'X counter'
print(d)

defaultdict(<class '__main__.NamedCounter'>, {'x': NamedCounter(name='X counter', dict={'b': 2})})

like image 41
Raniz Avatar answered Nov 12 '22 08:11

Raniz