Of course! In Python, the term "getitem" almost always refers to the special "dunder" (double underscore) method __getitem__. This method is the magic behind the square bracket notation ([]) used for accessing items in a sequence or mapping.

Let's break it down.
The Core Concept: __getitem__
When you write my_object[index], Python secretly calls the my_object.__getitem__(index) method behind the scenes. If you define this method in your own class, you make instances of your class "subscriptable," meaning they can be used with the [] operator.
How __getitem__ Works with Built-in Types
You don't need to define __getitem__ for Python's built-in types like lists, tuples, or dictionaries, but it's good to see how they use it.
# A simple list my_list = ['apple', 'banana', 'cherry'] # This line... print(my_list[1]) # ...is equivalent to calling this special method behind the scenes: print(my_list.__getitem__(1))
Output:

banana
banana
The same principle applies to other sequences and mappings:
- Tuples:
my_tuple[0]callsmy_tuple.__getitem__(0) - Strings:
my_string[5]callsmy_string.__getitem__(5) - Dictionaries:
my_dict['key']callsmy_dict.__getitem__('key')
Creating Your Own Subscriptable Class with __getitem__
This is where __getitem__ becomes incredibly powerful. You can create custom objects that behave like lists, dictionaries, or any other data structure.
Example 1: A Custom List-like Sequence
Let's create a class that represents a sequence of even numbers.
class EvenNumbers:
def __init__(self, limit):
self.limit = limit
def __getitem__(self, index):
# We need to handle both integer indexing and slicing
if isinstance(index, slice):
# If it's a slice, return a new list of the sliced items
start, stop, step = index.indices(self.limit)
return [i for i in range(start, stop, step) if i % 2 == 0]
elif isinstance(index, int):
# If it's an integer, return the item at that "logical" position
# The 0th even number is 0, the 1st is 2, the 2nd is 4, etc.
if index < 0:
# Handle negative indexing
index += self.limit // 2 + (1 if self.limit % 2 != 0 else 0)
if index < 0 or index >= self.limit // 2 + (1 if self.limit % 2 != 0 else 0):
raise IndexError("EvenNumbers index out of range")
return index * 2
else:
raise TypeError("Index must be an integer or a slice")
# --- Usage ---
evens = EvenNumbers(10) # Represents even numbers up to 10 (0, 2, 4, 6, 8)
# Accessing items by index
print(f"Item at index 0: {evens[0]}") # Output: Item at index 0: 0
print(f"Item at index 1: {evens[1]}") # Output: Item at index 1: 2
print(f"Item at index 4: {evens[4]}") # Output: Item at index 4: 8
print(f"Item at index -1: {evens[-1]}") # Output: Item at index -1: 8
# Using slicing
print(f"Slice [1:4]: {evens[1:4]}") # Output: Slice [1:4]: [2, 4, 6]
# This will raise an IndexError
try:
print(evens[5])
except IndexError as e:
print(e) # Output: EvenNumbers index out of range
Example 2: A Custom Dictionary-like Object
Here's a class that maps day numbers (1-7) to their names.

class DayMap:
def __init__(self):
# Internal data store
self._days = {
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday",
7: "Sunday"
}
def __getitem__(self, key):
# Simply delegate to the internal dictionary's __getitem__
return self._days[key]
def __setitem__(self, key, value):
# You can also define __setitem__ to make your object mutable
self._days[key] = value
# --- Usage ---
days = DayMap()
# Accessing an item
print(f"Day 3 is: {days[3]}") # Output: Day 3 is: Wednesday
# Modifying an item (because we defined __setitem__)
days[3] = "Hump Day"
print(f"Day 3 is now: {days[3]}") # Output: Day 3 is now: Hump Day
Advanced Indexing: Slicing with __getitem__
The index parameter in __getitem__ can be more than just an integer. It can also be a slice object (slice(start, stop, step)). This is how my_list[1:10:2] works.
Your __getitem__ method needs to check the type of the index argument to handle it correctly.
Here is a simplified example showing how to handle a slice:
class MySequence:
def __init__(self, data):
self.data = list(data)
def __getitem__(self, index):
print(f"__getitem__ called with index: {index!r} (type: {type(index).__name__})")
if isinstance(index, slice):
# The slice object itself has start, stop, and step attributes
print(f" -> Slice details: start={index.start}, stop={index.stop}, step={index.step}")
# We can apply the slice directly to our internal data
return self.data[index]
elif isinstance(index, int):
return self.data[index]
else:
raise TypeError("Invalid index type")
# --- Usage ---
seq = MySequence(range(10))
print("\nAccessing with an integer:")
item = seq[3]
# Output:
# __getitem__ called with index: 3 (type: int)
# -> Accessing item at position 3
print("\nAccessing with a slice:")
sliced = seq[2:8:2]
# Output:
# __getitem__ called with index: slice(2, 8, 2) (type: slice)
# -> Slice details: start=2, stop=8, step=2
# -> Returning [2, 4, 6]
print(f"\nResult of slice: {sliced}")
The NotImplementedError Alternative
For some objects, indexing might not make sense. Instead of raising a generic TypeError, it's more idiomatic to raise a NotImplementedError. This signals to the user (and other libraries) that the operation is intentionally not supported.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __getitem__(self, index):
# A point is not a sequence or mapping, so indexing is not supported.
raise NotImplementedError("A Point object is not subscriptable.")
p = Point(10, 20)
try:
value = p[0]
except NotImplementedError as e:
print(e) # Output: A Point object is not subscriptable.
Summary
| Concept | Description | Example |
|---|---|---|
__getitem__(self, key) |
The special method that enables object[key] syntax. |
my_list[0] |
key Parameter |
Can be an integer (for sequences), a string (for mappings), or a slice object. | my_dict['name'], my_arr[1:5] |
| Why Override It? | To create custom objects that behave like lists, dictionaries, or other data structures. | class CustomList: ... |
| Companion Method | __setitem__(self, key, value) enables assignment with object[key] = value. |
my_list[0] = 'new_value' |
| Companion Method | __delitem__(self, key) enables deletion with del object[key]. |
del my_list[0] |
When to Raise NotImplementedError |
When indexing is fundamentally not supported for your object's design. | class Point: ... |
