__setitem__
__setitem__(self, key, value) None · Added in vPython 1.0 · Updated March 18, 2026 · Keywords __setitem__
Python’s __setitem__ method lets you define what happens when you assign to a subscript—that is, when you use bracket notation to set a value: obj[key] = value.
Overview
__setitem__ is a special method that gets called automatically whenever you assign to a subscripted expression. It’s the setter counterpart to __getitem__, which handles reading values.
Python calls __setitem__ automatically when you use:
obj[key] = valueobj[key] += value(calls__setitem__after__getitem__)- Slice assignment like
obj[start:end] = values
Syntax
def __setitem__(self, key, value):
# Handle the assignment
# Typically doesn't return anything (returns None implicitly)
Key points:
- Takes three parameters:
self,key, andvalue - The
keycan be an integer, string, or even asliceobject - Does not need to return a value (returning a value is ignored)
How It Works
When you define __setitem__, you control what happens when someone assigns to your object using bracket notation. This is essential for building custom containers that behave like lists or dictionaries.
class ScoreBoard:
def __init__(self):
self.scores = {}
def __setitem__(self, player, score):
self.scores[player] = score
board = ScoreBoard()
board["Alice"] = 100 # Calls __setitem__
board["Bob"] = 85 # Calls __setitem__
print(board.scores)
Basic Example
Here’s a simple custom list that tracks assignments:
class TrackedList:
def __init__(self):
self.items = []
def __setitem__(self, index, value):
print(f"Setting index {index} to {value}")
self.items[index] = value
def __getitem__(self, index):
return self.items[index]
def __len__(self):
return len(self.items)
lst = TrackedList()
lst[0] = "first" # Setting index 0 to first
lst[1] = "second" # Setting index 1 to second
print(lst[0], lst[1]) # first second
# output: first second
Sequences vs Mappings
The type of exception you raise matters. Python distinguishes between sequences and mappings:
- Sequences (list-like): raise
IndexErrorfor invalid integer indices - Mappings (dict-like): raise
KeyErrorfor invalid keys
class IntList:
"""A list that only accepts integers."""
def __init__(self, size):
self.data = [None] * size
def __setitem__(self, key, value):
if not isinstance(key, int):
raise TypeError(f"indices must be integers, not {type(key).__name__}")
if key < 0 or key >= len(self.data):
raise IndexError(f"index {key} out of range for size {len(self.data)}")
if not isinstance(value, int):
raise TypeError(f"only integers allowed, got {type(value).__name__}")
self.data[key] = value
nums = IntList(3)
nums[0] = 10
nums[1] = 20
nums[2] = 30
nums[0] = "bad" # TypeError: only integers allowed
nums[5] = 100 # IndexError: index 5 out of range for size 3
Handling Slice Objects
When you assign to a slice like obj[1:4] = [a, b, c], the key parameter is a slice object. Handle it explicitly if you want slice support:
class SliceableList:
def __init__(self):
self.items = []
def __setitem__(self, key, value):
if isinstance(key, slice):
# Handle slice assignment
start, stop, step = key.indices(len(self.items))
self.items[start:stop:step] = value
else:
self.items[key] = value
def __getitem__(self, key):
return self.items[key]
def __len__(self):
return len(self.items)
lst = SliceableList()
lst.items = [1, 2, 3, 4, 5]
lst[1:4] = [20, 30, 40]
print(lst.items) # [1, 20, 30, 40, 5]
# output: [1, 20, 30, 40, 5]
Common Gotchas
Confusing __setitem__ with __setattr__
These are different! __setattr__ handles any attribute assignment (obj.x = value), while __setitem__ only handles subscript assignment (obj[key] = value).
class Example:
def __setattr__(self, name, value):
print(f"Setting attribute {name} = {value}")
object.__setattr__(self, name, value)
def __setitem__(self, key, value):
print(f"Setting item {key} = {value}")
object.__setattr__(self, key, value)
e = Example()
e.name = "Alice" # Calls __setitem__? No! Calls __setattr__
e["name"] = "Alice" # Calls __setitem__
# output: Setting attribute name = Alice
# output: Setting item name = Alice
Infinite recursion
Be careful not to call self.key = value inside __setattr__ or __setitem__—that triggers the same method again!
class Broken:
def __setitem__(self, key, value):
self[key] = value # INFINITE RECURSION!
# Broken()[0] = 1 # RecursionError: maximum recursion depth exceeded
Fix: Use a different storage mechanism:
class Fixed:
def __init__(self):
self._data = {}
def __setitem__(self, key, value):
self._data[key] = value # Safe - uses dict directly
Forgetting to handle the value parameter
Don’t ignore the value! You must actually store it somewhere:
class Forgotten:
def __setitem__(self, key, value):
pass # Silently ignores the value - usually a bug!
f = Forgotten()
f["x"] = 100
print(f._data) # AttributeError: 'Forgotten' object has no attribute '_data'
Practical Examples
Observable dictionary
class ObservableDict:
def __init__(self):
self._data = {}
self._changes = []
def __setitem__(self, key, value):
old = self._data.get(key, "<unset>")
self._data[key] = value
self._changes.append(f"{key}: {old} -> {value}")
def __getitem__(self, key):
return self._data[key]
def history(self):
return self._changes.copy()
d = ObservableDict()
d["name"] = "Alice"
d["age"] = 30
d["name"] = "Bob"
print(d.history())
# output: ['name: <unset> -> Alice', 'age: <unset> -> 30', 'name: Alice -> Bob']
Range-validated list
class BoundedList:
def __init__(self, max_value=100):
self._data = []
self._max = max_value
def __setitem__(self, key, value):
if not isinstance(value, (int, float)):
raise TypeError("only numeric values allowed")
if value < 0 or value > self._max:
raise ValueError(f"value must be between 0 and {self._max}")
# Expand list if necessary
while len(self._data) <= key:
self._data.append(None)
self._data[key] = value
def __getitem__(self, key):
return self._data[key]
def __len__(self):
return len(self._data)
scores = BoundedList(max_value=100)
scores[0] = 95
scores[1] = 87
print(scores[0], scores[1])
# output: 95 87
scores[2] = 150 # ValueError: value must be between 0 and 100
Summary
__setitem__defines behavior for subscript assignment:obj[key] = value- Takes three parameters:
self,key, andvalue - The key can be an integer, string, or slice object
- Raise
IndexErrorfor sequences with invalid indices - Raise
KeyErrorfor mappings with invalid keys - Avoid infinite recursion by using a different storage mechanism
- Different from
__setattr__which handles all attribute assignments
Use __setitem__ when building custom containers that need to respond to bracket notation assignment.