Last modified April 8, 2025
This comprehensive guide explores Python's __delitem__ method, the
special method responsible for item deletion in container objects. We'll cover
basic usage, dictionary operations, list operations, and custom implementations.
The __delitem__ method is called to implement deletion of items
using the del statement or similar operations. It allows objects to
define custom behavior when items are deleted by key or index.
Key characteristics: it takes the instance (self) and key/index as arguments,
modifies the object in-place, and typically returns None. It's invoked by
del obj[key] syntax.
Here's a simple implementation showing how __delitem__ works in a
custom container class. This demonstrates the basic structure and usage.
class MyContainer:
def __init__(self):
self.data = {'a': 1, 'b': 2, 'c': 3}
def __delitem__(self, key):
print(f"Deleting item with key: {key}")
del self.data[key]
container = MyContainer()
del container['b'] # Calls __delitem__
print(container.data) # {'a': 1, 'c': 3}
This example shows a container that wraps a dictionary. When we use del
on an item, it calls __delitem__ which then deletes from the
internal dictionary.
The method receives the key used in the deletion operation and performs the actual removal from the container's storage. No return value is needed.
For sequence types that support index-based deletion, __delitem__
can handle both integers and slices, similar to built-in lists.
class MyList:
def __init__(self, items):
self.items = list(items)
def __delitem__(self, index):
if isinstance(index, slice):
del self.items[index]
else:
del self.items[index]
def __repr__(self):
return f"MyList({self.items})"
lst = MyList([10, 20, 30, 40, 50])
del lst[1] # Delete single item
print(lst) # MyList([10, 30, 40, 50])
del lst[1:3] # Delete slice
print(lst) # MyList([10, 50])
This list-like class handles both single index deletion and slice deletion. The method checks the type of the index parameter to handle both cases properly.
The slice handling is identical to Python's built-in list behavior, making the class behave like a standard sequence type.
We can create a dictionary subclass that logs all deletion operations by
overriding __delitem__ while maintaining all dictionary behavior.
class LoggingDict(dict):
def __delitem__(self, key):
print(f"Log: Deleting key '{key}'")
super().__delitem__(key)
d = LoggingDict({'red': '#FF0000', 'green': '#00FF00', 'blue': '#0000FF'})
del d['green'] # Logs the deletion
print(d) # {'red': '#FF0000', 'blue': '#0000FF'}
This dictionary subclass adds logging before performing the actual deletion. It
uses super() to call the parent class's __delitem__.
This pattern is useful for debugging or tracking changes in dictionary-like objects without changing their core functionality.
__delitem__ can enforce constraints on what items can be deleted,
such as preventing deletion of certain protected keys.
class ProtectedContainer:
def __init__(self):
self._data = {'config': {}, 'values': {}}
self._protected = {'config'}
def __delitem__(self, key):
if key in self._protected:
raise KeyError(f"Cannot delete protected key: {key}")
del self._data[key]
def __repr__(self):
return str(self._data)
pc = ProtectedContainer()
pc._data['config']['timeout'] = 30
pc._data['values']['temp'] = 98.6
try:
del pc['config'] # Raises KeyError
except KeyError as e:
print(e)
del pc['values'] # Works
print(pc) # {'config': {'timeout': 30}}
This container protects certain keys from deletion by checking against a set of
protected keys before allowing the operation. It raises KeyError
for protected items.
This pattern is useful when you need to maintain certain internal state while allowing modification of other parts of the container.
For more complex data structures, __delitem__ can implement
sophisticated deletion logic, like removing entire rows or columns from a matrix.
class Matrix:
def __init__(self, rows, cols):
self.rows = [[0] * cols for _ in range(rows)]
self.row_count = rows
self.col_count = cols
def __delitem__(self, key):
if isinstance(key, tuple) and len(key) == 2:
row, col = key
self.rows[row][col] = 0
elif isinstance(key, int):
del self.rows[key]
self.row_count -= 1
else:
raise TypeError("Invalid key type")
def __repr__(self):
return '\n'.join(' '.join(map(str, row)) for row in self.rows)
m = Matrix(3, 3)
m.rows = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print("Original matrix:")
print(m)
del m[1] # Delete entire row
print("\nAfter deleting row 1:")
print(m)
del m[0, 1] # Delete single element (set to 0)
print("\nAfter deleting element at (0,1):")
print(m)
This matrix class handles two types of deletion: removing entire rows (by integer index) or zeroing out individual elements (by (row,col) tuple). The method checks the key type to determine the operation.
This demonstrates how __delitem__ can support multiple deletion
semantics in a single class, providing flexible container behavior.
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Python tutorials.