A while ago I turned to Python as my primary language. I've seen a lot of Python code before, but it was only when I was using it on a daily basis that I started to really appreciate it's beauty. One of the main reasons for this is its consistency. This consistency is what we commonly call pythonic.
Collections are a great example of how this consistency becomes apparent. There are three components at play that make Python collections pythonic:
- A comprehensive list of built-in collections (
- A set of built-in idioms (
- A mechanism for user defined sequences to take advantage of the built-in idioms.
The third component is probably the one that will take longer for new developers to find out. But once they do, writing Python becomes a lot more fun. Also, Python makes this very easy. There is a module called
collections.abc (documentation here) that define a set of abstract classes that can be extended in order to guarantee that a user defined collection remains pythonic.
Lets jump to a small example of how this can used in practice. Lets build a small class to that stores a collection of music albums.
import collections from collections.abc import MutableSequence Album = collections.namedtuple("Album", ["name", "artist", "year"]) class AlbumCollection(MutableSequence): def __init__(self, *albums): self.inner_list = list() self.inner_list.extend(albums) def __len__(self): return len(self.inner_list) def __getitem__(self, index): return self.inner_list[index] def __delitem__(self, index): del self.inner_list[index] def insert(self, index, value): self.inner_list.insert(index, value) def __setitem__(self, index, value): self.inner_list[index] = value
We're inheriting from
MutableSequence and only implementing the abstract methods for now. These are defined in the documentation, but it can also be useful to take a step ahead and look at the code.
Most of the behavior in our class is just delegating to a the
inner_list. But this is enough to illustrate how we can use some of the Python idioms with a custom type.
First, let's create an instance.
>>> album_collection = AlbumCollection( ... Album("Nevermind", "Nirvana", 1991), ... Album("OK Computer", "Radiohead", 1997), ... Album("Siamese Dreams", "The Smashing Pumpkins", 1993), ... Album("Superunknown", "Soundgarden", 1994) ... )
And see how we can immediately use some of the most popular Python collection idioms in our newly defined data structure.
>>> album_collection Album(name='OK Computer', artist='Radiohead', year=1997) >>> len(album_collection) 4 >>> in_utero = Album("In Utero", "Nirvana", 1993) >>> album_collection.append(in_utero) >>> in_utero in album_collection True >>> album_collection.remove(in_utero) >>> in_utero in album_collection False >>> album_collection[1:3] [Album(name='OK Computer', artist='Radiohead', year=1997), Album(name='Siamese Dreams', artist='The Smashing Pumpkins', year=1993)]
You get random access to items using the
 notation, there's support for
del, slices and much more.
Most of these idioms are made possible by the so called special methods (aka magic methods) like
__getitem__. You can learn more about this pattern in the Python Datamodel documentation.
This is only a small introduction but I hope it's representative of how beautifully practical Python can be when we start peaking under the hood.
Note: All the examples and links are for Python 3.5.