Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exposing `defaultdict` as a regular `dict`

I am using defaultdict(set) to populate an internal mapping in a very large data structure. After it's populated, the whole structure (including the mapping) is exposed to the client code. At that point, I don't want anyone modifying the mapping.

And nobody does, intentionally. But sometimes, client code may by accident refer to an element that doesn't exist. At that point, a normal dictionary would have raised KeyError, but since the mapping is defaultdict, it simply creates a new element (an empty set) at that key. This is quite hard to catch, since everything happens silently. But I need to ensure this doesn't happen (the semantics actually doesn't break, but the mapping grows to a huge size).

What should I do? I can see these choices:

  1. Find all the instances in current and future client code where a dictionary lookup is performed on the mapping, and convert it to mapping.get(k, {}) instead. This is just terrible.

  2. "Freeze" defaultdict after the data structure is fully initialized, by converting it to dict. (I know it's not really frozen, but I trust client code to not actually write mapping[k] = v.) Inelegant, and a large performance hit.

  3. Wrap defaultdict into a dict interface. What's an elegant way to do that? I'm afraid the performance hit may be huge though (this lookup is heavily used in tight loops).

  4. Subclass defaultdict and add a method that "shuts down" all the defaultdict features, leaving it to behave as if it's a regular dict. It's a variant of 3 above, but I'm not sure if it's any faster. And I don't know if it's doable without relying on the implementation details.

  5. Use regular dict in the data structure, rewriting all the code there to first check if the element is in the dictionary and adding it if it's not. Not good.

like image 551
max Avatar asked Nov 20 '12 02:11

max


People also ask

Is Defaultdict a dict?

A defaultdict works exactly like a normal dict, but it is initialized with a function (“default factory”) that takes no arguments and provides the default value for a nonexistent key. A defaultdict will never raise a KeyError. Any key that does not exist gets the value returned by the default factory.

What is the difference between dict and Defaultdict?

The main difference between defaultdict and dict is that when you try to access or modify a key that's not present in the dictionary, a default value is automatically given to that key . In order to provide this functionality, the Python defaultdict type does two things: It overrides .

How do I make a default dict a default dictionary?

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 .

Is Defaultdict slower than dict?

defaultdict is not necessarily slower than a regular dict . The timings there are flawed, as the timings include creating the object. Other than that, there are different types of performance, maintenance ease being one.


2 Answers

defaultdict docs say for default_factory:

If the default_factory attribute is None, this raises a KeyError exception with the key as argument.

What if you just set your defaultdict's default_factory to None? E.g.,

>>> d = defaultdict(int) >>> d['a'] += 1 >>> d defaultdict(<type 'int'>, {'a': 1}) >>> d.default_factory = None >>> d['b'] += 2 Traceback (most recent call last):   File "<stdin>", line 1, in <module> KeyError: 'b' >>>  

Not sure if this is the best approach, but seems to work.

like image 122
Neal Avatar answered Sep 22 '22 08:09

Neal


Once you have finished populating your defaultdict, you can simply create a regular dict from it:

my_dict = dict(my_default_dict) 

One can optionally use the typing.Final type annotation.

If the default dict is a recursive default dict, see this answer which uses a recursive solution.

like image 20
Asclepius Avatar answered Sep 19 '22 08:09

Asclepius