Python __imul__ Method
Last modified April 8, 2025
This comprehensive guide explores Python's __imul__
method, the
special method that implements in-place multiplication. We'll cover basic usage,
operator overloading, mutable vs immutable types, and practical examples.
Basic Definitions
The __imul__
method is called to implement the in-place multiplication
operation (*=
). It should modify and return self
when
possible, but can return a new object if necessary.
Key characteristics: it modifies the object in-place when possible, returns the
result (usually self), and is called for *=
operations. If not
implemented, Python falls back to __mul__
followed by assignment.
Basic __imul__ Implementation
Here's a simple implementation showing how __imul__
works with a
custom class. The method modifies the object's state and returns itself.
class Number: def __init__(self, value): self.value = value def __imul__(self, other): self.value *= other return self def __repr__(self): return f"Number({self.value})" num = Number(5) num *= 3 print(num) # Output: Number(15)
This example shows a basic __imul__
implementation that modifies
the instance's value attribute. The method returns self to allow chaining
operations.
The *=
operator calls __imul__
, which updates the
object's state in-place. This is more efficient than creating a new object.
__imul__ with Mutable Sequences
For mutable sequences like lists, __imul__
performs in-place
repetition. This example demonstrates the behavior with a custom sequence.
class MyList: def __init__(self, items): self.items = list(items) def __imul__(self, factor): self.items *= factor return self def __repr__(self): return f"MyList({self.items})" lst = MyList([1, 2]) lst *= 3 print(lst) # Output: MyList([1, 2, 1, 2, 1, 2])
This custom list class implements __imul__
to multiply its contents
in-place. The original object is modified rather than creating a new one.
The implementation delegates to the built-in list's *=
operation,
which efficiently handles the repetition. This pattern is common for wrappers.
__imul__ with Immutable Types
Immutable types can't be modified in-place, so their __imul__
must
return a new object. This example shows the behavior difference.
class ImmutableNumber: def __init__(self, value): self.value = value def __imul__(self, other): return ImmutableNumber(self.value * other) def __repr__(self): return f"ImmutableNumber({self.value})" num = ImmutableNumber(5) num *= 3 print(num) # Output: ImmutableNumber(15) print(id(num)) # Shows a new object was created
Since immutable objects can't change their state, __imul__
returns
a new instance. The original object remains unchanged, and the variable is
reassigned.
This matches Python's built-in behavior for immutable types like tuples, where
*=
creates a new object rather than modifying in-place.
Matrix Multiplication with __imul__
For mathematical objects like matrices, __imul__
can implement
in-place matrix multiplication. This example shows a simplified version.
class Matrix: def __init__(self, data): self.data = data def __imul__(self, other): if isinstance(other, (int, float)): # Scalar multiplication self.data = [[x * other for x in row] for row in self.data] return self # Matrix multiplication would go here raise TypeError("Unsupported operand type") def __repr__(self): return f"Matrix({self.data})" m = Matrix([[1, 2], [3, 4]]) m *= 2 print(m) # Output: Matrix([[2, 4], [6, 8]])
This matrix class implements scalar multiplication in-place through
__imul__
. The method checks the operand type and performs
the appropriate operation.
For actual matrix multiplication, you would need to implement the full algorithm, but this shows the in-place modification pattern.
Combining __imul__ with Other Operations
__imul__
can be combined with other operations for complex
behavior. This example shows a class that tracks multiplication history.
class TrackingNumber: def __init__(self, value): self.value = value self.history = [] def __imul__(self, other): self.history.append((self.value, other)) self.value *= other return self def get_history(self): return self.history def __repr__(self): return f"TrackingNumber({self.value})" num = TrackingNumber(2) num *= 3 num *= 4 print(num) # Output: TrackingNumber(24) print(num.get_history()) # Output: [(2, 3), (6, 4)]
This class extends the basic number behavior by tracking all in-place multiplications. The history is stored in a list and updated during each operation.
The example demonstrates how __imul__
can maintain additional
state beyond just performing the mathematical operation. This is useful for
debugging or auditing.
Best Practices
- Modify in-place when possible: Follow Python's mutable object conventions
- Return self: Allows method chaining and matches built-in behavior
- Handle different types: Check operand types and raise TypeError if needed
- Consider immutability: Return new objects for immutable types
- Document behavior: Clearly specify whether operation is in-place
Source References
Author
List all Python tutorials.