The defaultdict subclass

An ordinary dict type raises an exception when a key is not found. A defaultdict collection class does this differently. Instead of raising an exception, it evaluates a given function and inserts the value of that function into the dictionary as a default value.


Class names are usually in upper TitleCase. However, the defaultdict class doesn't follow this pattern.

A common use case for the defaultdict class is to create indices for objects. When several objects have a common key, we can create a list of objects that share this key.

Here's a function that accumulates a list of distinct values based on a summary of the two values:

from typing import Dict, List, Tuple, DefaultDict
def dice_examples(n: int=12, seed: Any=None) -> DefaultDict[int, List]:
if seed:
random.seed(seed)
Roll = Tuple[int, int]
outcomes: DefaultDict[int, List[Roll]] = defaultdict(list)
for _ in range(n):
d1, d2 = random.randint(1, 6), random.randint(1, 6)
outcomes[d1+d2].append((d1, d2))
return outcomes

The type hint for Roll shows that we consider a roll of the dice to be a two-tuple composed of integers. The outcomes object has a hint that it will be a dictionary that has integer keys and the associated value will be a list of Roll instances.

The dictionary is built using outcomes[d1+d2].append((d1, d2)). Given two random numbers, d1 and d2, the sum is the key value. If this key value does not already exist in the outcomes mapping, the list() function is used to build a default value of an empty list. If the key already exists, the value is simply fetched, and the append() method is used to accumulate the actual pair of numbers.

As another example, we can use the a defaultdict collection class to provide a constant value. We can use this instead of the container.get(key,"N/A") expression.

We can create a zero-argument lambda object. This works very nicely. Here's an example:

>>> from collections import defaultdict 
>>> messages = defaultdict(lambda: "N/A") 
>>> messages['error1']= 'Full Error Text' 
>>> messages['other'] 
'N/A'
>>> messages['error1']
'Full Error Text'

In the first use of messages['error1'], a value was assigned to the 'error1' key. This new value will replace the default. The second use of messages['other'] will add the default value to the dictionary. 

We can determine how many new keys were created by looking for all the keys that have a value of "N/A":

>>> [k for k in messages if messages[k] == "N/A"] 
['other'] 

As you can see in the preceding output, we found the key that was assigned the default value of "N/A". This is often a helpful summary of the data that is being accumulated. It shows us all of the keys associated with the default value.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset