The Manor House, the Oak-Panelled Library, the Vending Machine, and Python's __getitem__() [Part 1]
Understanding how to use the Python special method __getitem__(). The first of a two-part article
We spent a long weekend in an English countryside manor house last month. After dropping my bags in the room, I went to explore. I spotted the sign saying "Library", and of course, I followed the arrow. As I stepped into the wood-panelled room, I could feel the sense of timeless serenity and tranquillity.
Light filtered in through glass windows, rows upon rows of built-in oak bookshelves stretched from floor to ceiling, filled to the brim with venerable volumes. Their bindings showed a spectrum of colours faded into pastels, the gold lettering on the spines catching the light. This was the perfect spot for a weekend of reading. I looked around to find my "perfect" spot. Then I saw it—no, not the sought-out idea armchair; I spotted something else.
There was a hideous vending machine selling oily crisps and sugary drinks fitted into one of the bookcases. Why?! But why?
I'll let you in on a secret. This monstrosity doesn't exist. Trust me, if it did, I would have run out of the manor and never looked back. But, if you can live with the revulsion, let's stay in this library a bit longer. It will help us explore Python's __getitem__()
special method.
I'll assume you're familiar with the basics of object-oriented programming (OOP). But if you need to read more, you can read Chapter 7 | Object-Oriented Programming in The Python Coding Book or the Harry Potter-themed series of OOP articles here on The Python Coding Stack.
• Author’s note: I’ve recently started a new Substack which focusses on my thoughts and ideas on technical writing, and my experiments with using storytelling techniques when writing about Python. If you’re reading this, you’re likely interested in Python and you may not be interested in the process of technical writing itself. Fair enough. But if you happen to also be interested in narrative technical writing, you can subscribe to
•Vending Machine • First Attempt
Let's create a basic class to represent a vending machine:
The VendingMachine
class has an __init__()
method, which creates the data attribute .items_on_sale
and the method add_items()
. The items for sale in the vending machine are stored in a list. For now, let's assume for simplicity that the slots in the vending machine are numbered sequentially, starting from zero. Therefore, the item's number in the machine matches its list index.
You can stock the vending machine with a few items. I'll replace code that's unchanged from the previous code blocks in this article with # ...
for brevity and clarity:
You could write a method to access an item from the vending machine. But what if you'd like to use the square bracket notation [ ]
to fetch a value from this VendingMachine
object in the same way you would with built-in types such as lists and tuples?
Let's try it out to see what happens:
Unfortunately, this won't work. You get a TypeError
:
TypeError: 'VendingMachine' object is not subscriptable
You can't subscript this object. This means you can't use square brackets [ ]
to fetch a value.
But there's nothing magical about using square brackets like the ones to fetch items from lists or tuples. There is a special method associated with this notation that we'll explore in the next section.
Defining __getitem__()
Let's define the __getitem__()
special method. This is one of the methods that have double underscores at the beginning and end of the method name. As the name implies, these methods are special:
The __getitem__()
special method accepts two parameters:
self
: the first parameter used by convention in all instance methods. This refers to the instance itself.idx
: the number of the slot in the vending machine. This matches the index in the listself.items_on_sale
.
This special method enables you to use the square brackets notation [ ]
to refer to an item within the VendingMachine
instance. The value you put in the square brackets is passed to the parameter named idx
. The __getitem__()
method returns the value required.
Let's try running the code now. I'm showing the code as it stands at the moment. There are no new additions in the following code block.:
The output from this code is the following:
Still Water
Now you can access an item from the vending machine using the square brackets notation. You use the index 2
, which returns "Still Water"
, the third item in the vending machine.
Let's look at the following expression:
hideous_machine[2]
This expression is equivalent to the following one:
hideous_machine.__getitem__(2)
Therefore, when you use the square brackets notation, the program calls a special method. The expression returns the value returned by __getitem__()
.
Let's finish off this section with one more addition to __getitem__()
:
You raise a TypeError
if the value passed to the special method, which is the same value in the square brackets, is not an integer. You could also handle the case when the index is out of bounds and raise an IndexError
. But in this case, I'll leave this out of the method and let the list handle this scenario.
Note that using the square brackets, and therefore __getitem__()
, does not remove the item from the vending machine. You need another method to remove an item when someone buys it. You'll deal with this in a later version of the VendingMachine
class.