__getitem__
Python calls __getitem__ whenever you write obj[key]. The subscript can be an integer, a slice, a string, or any other key type — Python defers entirely to your implementation.
Signature
def __getitem__(self, subscript):
...
Python translates bracket notation into a call to this method before it reaches your code. When you write container[0], Python calls container.__getitem__(0). When you write container["name"], it calls container.__getitem__("name").
Basic Subscription
A minimal __getitem__ implementation wraps an internal list or dict:
class Bag:
def __init__(self, items):
self._items = items
def __getitem__(self, key):
return self._items[key]
bag = Bag(["a", "b", "c"])
print(bag[0]) # a
print(bag[-1]) # c
This gives you read-only subscription. The wrapped list handles integer index bounds automatically, raising IndexError when you go out of range.
Slicing
Slicing works automatically if your underlying container supports it:
grid = Bag([[1, 2], [3, 4], [5, 6]])
print(grid[0]) # [1, 2]
print(grid[1:]) # [[3, 4], [5, 6]]
When you write obj[1:3], Python passes a slice object instead of an integer. The slice has three read-only attributes: start, stop, and step. You can pass it directly to a list:
def __getitem__(self, key):
if isinstance(key, slice):
return self._items[key]
return self._items[key]
Raising the Right Exceptions
This matters more than most developers realise. The Python data model specifies two distinct error paths:
| Situation | Exception to raise |
|---|---|
| Subscript type not supported (e.g., string key in a sequence) | TypeError |
| Key valid type but out-of-bounds or missing | LookupError subclass — IndexError for sequences, KeyError for mappings |
For a mapping-like object, raise KeyError (which is a subclass of LookupError), not ValueError or a custom exception:
class Config:
def __init__(self, data):
self._data = data
def __getitem__(self, key):
if key not in self._data:
raise KeyError(key)
return self._data[key]
Bounds Checking
For fixed-size containers, you need to enforce bounds yourself:
class FixedList:
def __init__(self, size):
self._size = size
def __getitem__(self, index):
if index < 0:
index = self._size + index
if index < 0 or index >= self._size:
raise IndexError(f"index {index} out of range for size {self._size}")
return index * 2
fl = FixedList(5)
print(fl[0]) # 0
print(fl[4]) # 8
print(fl[5]) # IndexError
Python passes negative indices through to your __getitem__ method. If your object behaves like a sequence, convert negative indices yourself and then check bounds, as shown above.
Iteration Protocol
__getitem__ and __len__ together implement the sequence iteration protocol. for loops start at index 0 and increment until __getitem__ raises IndexError:
class Counter:
def __init__(self, stop):
self._stop = stop
def __len__(self):
return self._stop
def __getitem__(self, index):
if index >= self._stop:
raise IndexError(index)
return index
for i in Counter(3):
print(i)
# output: 0
# output: 1
# output: 2
If you omit __len__, for loops will call __getitem__ starting at 0 and keep going forever — unless you raise IndexError at some point yourself.
__contains__ vs __getitem__
The in operator first checks __contains__ if it exists, then falls back to iteration. Don’t rely on __getitem__ returning a truthy value to make in work:
class Wrapper:
def __init__(self, data):
self._data = data
def __getitem__(self, key):
return self._data[key]
def __contains__(self, item):
return item in self._data
w = Wrapper([1, 2, 3])
print(2 in w) # True
print(5 in w) # False
Summary
| Aspect | Detail |
|---|---|
| Called when | You write obj[key] |
Parameter subscript type | int, slice, or any hashable key depending on your object |
| Slicing | Python passes a slice object; pass it directly to your underlying container |
| Wrong type exception | TypeError |
| Wrong value / out-of-bounds exception | IndexError (sequences) or KeyError (mappings) |
Iteration with __len__ | for loop increments from 0 until IndexError |
See Also
__setitem__— assign toobj[key]__delitem__— deleteobj[key]__len__— enableslen(obj)__contains__— enablesitem in obj