Duck Typing in Python — 3 Practical Examples

Original article can be found here (source): Artificial Intelligence on Medium

Practical Examples

Iterators

In Python, iteration allows us to go over a list of items to perform particular operations. One common way to create an iteration is to use the for loops, which have the following general format.

for i in iterable:
expression

As discussed in my previous article, we can create our custom iterators that can be used as iterables in the for loops. To satisfy the “duck testing” for iterators, the custom class needs to implement the __iter__() and __next__() methods. A specific example is given below.

Duck Typing in Iterators

In the above code snippet, we implemented both __iter__() and __next__() methods that make the instances of the custom class Cubes iterators, such that the instance was able to be used in a for loop.

Callables

Besides the built-in dict data type, another important dictionary type is defaultdict, available in the collections module. This dictionary-like data type has the following constructor: defaultdict([default_factory[, ...]]). Specifically, the default_factory argument is a type of callable, such as a function or lambda function.

In my previous article, I showed that we could utilize the defaultdict by passing a lambda function. Here’s the example of its usage.

>>> from collections import defaultdict
>>> letter_counts = defaultdict(lambda: 0)
>>> for i in 'abcddddeeeee':
... letter_counts[i] += 1
...
>>> letter_counts.items()
dict_items([('a', 1), ('b', 1), ('c', 1), ('d', 4), ('e', 5)])

Notably, we can have better flexibility to use the defaultdict data type if we create our own default factory. With the “duck typing” philosophy, the custom class needs to implement the __call__() method, which is to make something callable. Let’s see how it works.

Duck Typing in Callables

As shown in the above code, we create the DucklingFactory class, whose __call__() function returns a list of one Duckling instance. Using this factory function, we’ll be able to make the desired number of ducklings by multiplying the default duckling list.

Sorting With Len()

Another possible usage of duck typing is to implement the custom len() function, which can be used in sorting a list using the sort() function. As a side note, some people refer to the len() function as a magic function, because it’s implemented by calling the __len__() function behind the scene (those functions with double underscores as prefix and suffix are called magic functions).

Suppose that we want to sort a list of ducks based on the length of the name of each duck. Here’s how we can apply the duck typing in this case.

Duck Typing in Sorting

In the above code snippet, the custom NamedDuck class implements the __str__() and __len__() functions, which enable us to use the str() and len() functions on its instances in Lines 10 and 12, respectively. Importantly, as you can see, although the list is mixed with named ducks and strings, all elements can call the str() and len() functions, such that the list can be sorted using the len() as the key and used in the list comprehension when we want to print out the sorted result.

Thus, the broader implication of this usage is that when we have a mixed list of elements of different data types, as long as each data type implements the same functions, the elements will pass the duck testing and be applied with the proper operations. Here’s a general use case example using the len() function on a mixed list of data types of str, tuple, and list.

>>> mixed_list = ['Amazing', (1, 2), ['hello']]
>>> mixed_list.sort(key=len)
>>> mixed_list
[['hello'], (1, 2), 'Amazing']