Unravelling subscriptions in Python
For the next post in my syntactic sugar series I want to cover subscriptions. It's quite possible you're not familiar with this formal term, but you are probably familiar with the syntax: the square brackets used for indexing lists and tuples (
sequence), accessing the value of a specified dictionary (
dictionary["key"]), etc. To cover this topic we will break up into three parts: general subscriptions, slicing, and multiple arguments.
A subscription can get, set, or delete items from a collection. These three operations have equivalent special methods called
__delitem__, respectively. Due to the fact that if a subscription is done on an object that does not have an appropriate special method, we will re-implement the appropriate functions from the
operator module. All three functions take a similar approach, so I will just show how
__getitem__ works and let you look at the source code for the other two functions.
- Gets the type of the container.
- Gets the
__getitem__method from the type.
- If the method doesn't exist, raise
- Otherwise call the method appropriately.
The syntax for slicing maps to the
slice class' constructor where any empty value is represented by
slice(None, None, None)
slice(1, 2, 3)
slice(None, 1)(maps to
The slice object then gets passed into the appropriate special method, so
x[1:2:3] is the equivalent of
type(x).__getitem__(x, slice(1, 2, 3)).
If you don't work with the scientific stack and use packages like NumPy, you may not know that you can actually pass in multiple arguments when using the subscription syntax:
[1, 2, 3]. The key difference to a function call, though, is all of the values get bundled up into a tuple that gets passed in as the first argument to the appropriate special method. This translates
x[1, 2, 3] to
type(x).__getitem__((1, 2, 3)). This also means that passing in a tuple with the same values is no different:
x[1, 2, 3] == x[(1, 2, 3)].