__len__, __getitem__, __setitem__

RoadmapsPython

Overview

In Python, using square brackets like `obj[key]` and calling `len(obj)` do not have special compiler-level behavior for custom classes. Instead, they are routed directly to specific 'dunder' (double underscore) methods on the object: `__getitem__(self, key)`, `__setitem__(self, key, value)`, and `__len__(self)`. class RowSet: def __init__(self, rows): self.rows = rows def __getitem__(self, index): return self.rows[index] def __len__(self): return len(self.rows) rs = RowSet(['a', 'b', 'c']) print(rs[0]) # 'a' -- calls rs.__getitem__(0) print(len(rs)) # 3 -- calls rs.__len__() Any custom class that implements these methods will seamlessly behave like a built-in list or dictionary — no inheritance required. Analogy: Dunder methods are like standardized electrical outlets in a house. You don't need to know how the electricity is generated inside the appliance; as long as the appliance has the right plug (implements the right dunder method), you can plug it into the wall (use standard Python syntax like `len()` or `[]`).

Implementing these methods allows custom objects to integrate natively with Python's built-in syntax. This makes your code more intuitive and 'Pythonic' by relying on standard operators instead of custom methods like `obj.get_value(key)`.

Where used: Custom configuration managers, Data processing pipelines, Wrappers around external APIs

Why learn this

Code walkthrough

class TrainCar:
  def __init__(self, passengers):
    self.passengers = passengers

  def __getitem__(self, index):
    return self.passengers[index]

car = TrainCar(['Alice', 'Bob'])
print(car[0])

Focus: print(car[0])

Aha moment

class MagicBox:
  def __getitem__(self, key):
    return key * 2

box = MagicBox()
print(box[10])
print(box['hello '])

Prediction: What happens when we access `box[10]` and `box['hello ']` even though `MagicBox` has no internal data?

Common guess: It raises an IndexError or KeyError because the box is empty and has no underlying list or dictionary.

`__getitem__` is simply a method call. It doesn't actually need to look up data in a dictionary or list; it receives the key inside the brackets as an argument and can return any dynamically computed value.

Common mistakes

Glossary

dunder
A special built-in method in Python that has double underscores before and after its name, e.g. `__len__` or `__getitem__`.
Pythonic
Writing code in a way that is natural, concise, and follows the best practices of the Python programming language, e.g. using `len(obj)` instead of `obj.get_length()`.

Recall questions

Understanding checks

What will the following code output?

40

The square bracket syntax `box[1]` internally calls the `__getitem__` method with `1` as the `index` argument. The method accesses `self.contents[1]` which is `20`, multiplies it by `2`, and returns `40`.

The following class is intended to allow getting its length using `len(bag)`. However, it raises a TypeError. What is wrong?

`__len__` must return an integer, but it is returning a string.

Python strictly enforces that the `__len__` dunder method returns a non-negative integer. Returning a string causes a TypeError when the built-in `len()` function is called on the object.

Practice tasks

Implement a simple Dictionary Wrapper

The `Config` class uses custom methods to read, set, and get the length of the data. Modify the class to use standard python syntax by replacing these methods with the appropriate `__getitem__`, `__setitem__`, and `__len__` dunder methods.

Challenge

A Repeating Sequence

Create a class `Repeater` initialized with a string. Its `__len__` should return the length of the string. When accessed with `__getitem__`, it should return the character at the given index, wrapping around indefinitely if the index is larger than the string length. (Assume positive indices). For example, `Repeater('abc')[4]` should return `'b'`.

Continue learning

Previous: Everything is an object in Python

Previous: Defining functions and return values

Previous: Lists: indexing, slicing, methods

Previous: Dictionaries: operations and use cases

Previous: __init__ and __new__

Next: The Iterable Protocol

Return to Python Roadmap