Back to Blog
Python

Python Augmented Assignment Operators Explained

python augmented assignment operators: Understand how Python augmented assignment operators like += and *= behave for mutable and immutable objects, including in-place...

augmented assignmentpython operatorsin-place operationsmutable objectspython syntax
Python code snippet showing an augmented assignment operator with a list being modified in place, illustrated with a visual metaphor of a container being filled.

Python augmented assignment operators, such as +=, -=, *=, and /=, combine an arithmetic operation with an assignment in a single statement. They are a common source of confusion because their behavior depends on whether the target object is mutable or immutable. Understanding the underlying mechanics is essential for writing predictable code, especially when working with lists, custom classes, or performance-sensitive loops.

The Basic Syntax and Its Equivalence

An augmented assignment like x += y is syntactically equivalent to x = x + y for most simple cases, but the runtime behavior can differ. The key distinction is that the augmented version may perform the operation in place on the target object, rather than creating a new object and rebinding the name. This is not just an implementation detail; it affects whether other references to the same object see the change.

Consider a simple integer:

x = 5 x += 3 print(x) # 8

Here, x is rebound to a new integer object 8. The original 5 is discarded. For immutable types like integers, strings, and tuples, x += y is exactly equivalent to x = x + y because the operation cannot modify the original object.

Mutable Objects: In-Place vs Rebind

For mutable objects, the behavior changes. Lists, dictionaries, and sets can be modified in place. The augmented assignment operator attempts to use the in-place method if it exists. For example, list.__iadd__ is defined, so list += iterable extends the list in place.

a = [1, 2] b = a a += [3] print(a) # [1, 2, 3] print(b) # [1, 2, 3] # b sees the change because a was modified in place

Contrast this with the non-augmented version:

a = [1, 2] b = a a = a + [3] print(a) # [1, 2, 3] print(b) # [1, 2] # b still refers to the original list

In the second example, a + [3] creates a new list, and a is rebound to that new list. The original list, still referenced by b, remains unchanged. This distinction is critical when passing lists to functions or storing them in multiple variables.

How Python Resolves Augmented Assignment

The augmented assignment operator follows a specific resolution order. For a target like obj.attr += value or obj[key] += value, Python evaluates the target once, then calls the appropriate method. The process is:

  1. Evaluate the target object and, if needed, the subscript or attribute.
  2. Try to call the in-place method (__iadd__, __isub__, etc.).
  3. If the in-place method is not defined, fall back to the regular binary operation (__add__, __sub__, etc.) and assign the result back to the target.

This means that for a custom class, you can control whether += modifies the instance or returns a new one by defining __iadd__. If you do not define __iadd__, Python will use __add__ and then assign the result, which effectively rebinds the name.

Custom Classes and In-Place Semantics

When designing a class, you can implement __iadd__ to support in-place mutation. This is useful for objects that represent collections or buffers. Here is a minimal example:

class Counter: def __init__(self, value=0): self.value = value def __iadd__(self, other): self.value += other return self def __add__(self, other): return Counter(self.value + other)

With this class, c += 5 calls __iadd__ and modifies the existing instance. Without __iadd__, c += 5 would call __add__ and create a new Counter object, leaving the original unchanged. The choice depends on whether the object should be treated as a mutable container or an immutable value.

Evaluation Order and Subscript Targets

For augmented assignments on subscripts, the target expression is evaluated only once. This is different from the naive reading of a[i] += x as a[i] = a[i] + x, which would evaluate a and i twice. Python guarantees that the target is evaluated once, which matters when the target involves a function call or a side effect.

def get_list(): print("get_list called") return [1, 2] get_list()[0] += 10 # prints "get_list called" only once

This single evaluation is a deliberate language design choice and can affect performance in loops where the target is a complex expression.

Performance and Memory Implications

For immutable types, augmented assignment always creates a new object. In a loop that repeatedly applies += to a string or integer, each iteration allocates a new object, which can lead to quadratic behavior if the operation is O(n). For example, building a string with += in a loop is inefficient because each concatenation creates a new string and copies the entire previous content. The common workaround is to collect parts in a list and join them at the end.

For mutable objects like lists, += is generally more efficient than + because it avoids creating a new list and copying elements. The list.__iadd__ method uses extend, which can be amortized O(n) for the appended elements. However, be aware that += on a list will modify the list in place, which may not be desired if the original list is shared or if you need to keep the original unchanged.

When working with NumPy arrays, += performs in-place element-wise addition, which avoids allocating a new array. This is a significant performance advantage in numerical code. But the same in-place semantics mean that aliasing issues can arise if multiple variables reference the same array.

Common Mistakes and How to Avoid Them

The most common mistake is assuming that += always creates a new object. This leads to subtle bugs when sharing mutable objects. Another mistake is using += on a tuple, which raises a TypeError because tuples are immutable and do not support item assignment. For example:

t = (1, 2) t += (3,) # TypeError: 'tuple' object does not support item assignment

This error occurs because tuple has no __iadd__, and __add__ returns a new tuple, but the assignment back to t would require modifying the original tuple, which is not allowed. In practice, you should not use augmented assignment on tuples; use a new variable instead.

Another edge case is using augmented assignment on a slice of a list. The behavior is equivalent to replacing that slice with the result of the operation, which can be surprising if the slice is not a simple range. For instance:

lst = [1, 2, 3, 4] lst[1:3] += [10] print(lst) # [1, 2, 3, 10, 4]

The slice [1:3] is replaced by the concatenation of the slice and the new list. This is consistent with how slice assignment works, but it may not be immediately obvious.

When to Prefer Explicit Assignment

While augmented assignment is concise, there are situations where an explicit x = x + y is clearer. If you intend to create a new object and leave the original unchanged, using the explicit form makes that intention obvious. For example, when processing a list without mutating the original, you should write new_list = old_list + [item] rather than new_list = old_list; new_list += [item], which would mutate the original list if it is shared.

In code that is reviewed by others, the in-place semantics of augmented assignment can hide side effects. A reviewer might not immediately realize that items += new_items modifies the list in place. If the function is supposed to be non-mutating, use items = items + new_items or items.extend(new_items) with a clear comment. The choice should be driven by whether the object's identity must be preserved.

For custom classes, defining __iadd__ and __add__ with distinct behaviors gives you fine-grained control. If your class represents a value that should be immutable, omit __iadd__ so that += falls back to __add__ and returns a new instance. If it represents a mutable container, implement __iadd__ to modify in place and return self. This decision affects not only performance but also the class's contract with its users.

Finally, remember that augmented assignment is a statement, not an expression. You cannot use it inside a lambda or a list comprehension. If you need to update a variable as part of an expression, you must use a regular assignment or a helper function. This limitation is rarely a problem in practice, but it is worth keeping in mind when refactoring code.

python augmented assignment operators: Practical Usage and C | RYUSLOG DEV