For years, I’ve been using the following snippet in the solution for one of my interview questions:
anagrams = dict()
with open(WORDS_PATH) as f:
for line in f:
key = sort(line.strip())
if key not in anagrams:
anagrams[key] = list()
anagrams[key].append(line.strip())
else:
anagrams[key].append(line.strip())
Recently, I learned to use the dict.setdefault function to further optimize it, and the end result looks like the following:
anagrams = dict()
with open(WORDS_PATH) as f:
for line in f:
key = sort(line.strip())
anagrams.setdefault(key, []).append(line.strip())
Get the list of anagrams for key, or set it to [] if not found; setdefault returns the value, so it can be updated without requiring a second search. In other words, the end result of this line…
anagrams.setdefault(key, []).append(line.strip())
…is the same as running…
if key not in anagrams:
anagrams[key] = list()
anagrams[key].append(line.strip())
else:
anagrams[key].append(line.strip())
…except that the latter code performs at least two searches for key – three if it’s not found – while setdefault does it all with a single lookup.
Python has this idea of abstract base classes (ABCs), which define a set of methods and properties that a class must implement in order to be considered a duck-type instance of that class. The class can extend the abstract base class itself in order to be used as an instance of that class, but it must supply all the appropriate methods.
Case study
Recently, I worked on an internal tool to parse source files containing test cases written in three different kinds of testing frameworks i.e. pytest, Robot Framework, and Cucumber. Since the tool needs to parse source files with three different parsers with a same set of APIs, it is advisable to create an abstract base class in this case to document what APIs the parsers should provide (documentation is one of the stronger use cases for ABCs). The abc module provides the tools I need to do this, as demonstrated in the following block of code:
Though I omitted the implementation of the filter_test_case_from_source_file() method for brevity, the idea is that any subclass of the Parser class must implement this method, as it is decorated with @abstractmethod.
More common object-oriented languages (like Java, Kotlin) have a clear separation between the interface and the implementation of a class. For example, some languages provide an explicit interface keyword that allows us to define the methods that a class must have without any implementation. In such an environment, an abstract class is one that provides both an interface and a concrete implementation of some, but not all, methods. Any class can explicitly state that it implements a given interface. Python’s ABCs help to supply the functionality of interfaces without compromising on the benefits of duck typing.