+= -= *= /=
All fifteen forms follow one rule: try to mutate in place via __iop__, and fall back to rebinding. Whether you get mutation or rebinding depends on the TYPE, and that is where every surprise comes from.
Common call
count += 1
Returns
nothing — it is a statement
Replaces
a = a + b, but not always equivalently
Watch out
on a list it mutates in place, so every alias sees the change
aa — Target. Must already be bound — augmented assignment reads before it writes.type: Any · required opop — One of + - * / // % ** >> << & | ^ @, giving the fifteen forms.type: str · required= bb — Right operand, passed to __iop__ or the plain binary operator.type: Any · required
→ None
Operands
| Name | Type | Required | Description |
|---|---|---|---|
| a | Any | yes | Target. Must already be bound — augmented assignment reads before it writes. |
| op | str | yes | One of + - * / // % ** >> << & | ^ @, giving the fifteen forms. |
| b | Any | yes | Right operand, passed to __iop__ or the plain binary operator. |
Return value
None — A statement, not an expression — it produces no value and cannot appear inside a larger expression.
Examples
1. Increment
n = 1
n += 1
n
Returns
22. List mutates
a = [1]
b = a
a += [2]
b
Returns
[1, 2] # b changed too3. Concat rebinds
a = [1]
b = a
a = a + [2]
b
Returns
[1] # b unchanged4. Strings rebind
s = 'a'
s += 'b'
s
Returns
'ab' # a new object5. Undefined name
del q
q += 1
Returns
NameError: name 'q' is not defined6. Not an expression
print(n += 1)
Returns
SyntaxError: invalid syntaxPitfalls
1. x += y and x = x + y are NOT the same
The most important thing on this page. For a list, += calls __iadd__ and mutates in place, so every other reference sees the change. Plain concatenation builds a new list and rebinds only your name.
Alias sees it
a = [1] b = a a += [2] b
[1, 2]
Rebind instead
a = [1] b = a a = a + [2] b
[1]
2. The tuple trap — it raises AND mutates
The most notorious corner in the language. t[0] += [9] first mutates the inner list through __iadd__, then fails trying to store the result back into the immutable tuple. You get a TypeError and the mutation, which looks impossible until you know the order.
Both happen
t = ([1], 2) t[0] += [9] # TypeError raised t
([1, 9], 2) # changed anyway
Mutate explicitly
t = ([1], 2) t[0].extend([9]) t
([1, 9], 2), no error
3. It reads the target first
Augmented assignment is not a definition. The name must already be bound, otherwise you get a NameError — and inside a function, assigning to a global this way needs a global declaration.
Not yet bound
total += 1
NameError: name 'total' is not defined
Initialise first
total = 0 total += 1
1
4. It is a statement, so it has no value
Unlike C, you cannot use it inside an expression. Where you want assignment as part of a larger expression, the walrus operator is the tool Python provides.
No value
print(n += 1)
SyntaxError: invalid syntax
Walrus for expressions
print(n := n + 1)
the new value
5. Immutable types always rebind
Numbers, strings and tuples have no in-place form, so += silently builds a new object. Repeated string += in a loop is therefore quadratic, which is why join exists.
Quadratic
s = "" for x in items: s += x
a new string every iteration
Join once
s = "".join(items)
linear
When to use
Use it
- Counters and accumulators
- Extending a list in place when aliases SHOULD see the change
- Bitmask updates with |= and &=
- Anywhere a = a + b would repeat a long target expression
Reach for something else
- Building a string in a loop → join
- When aliases must NOT see the change → rebind with a = a + b
- Items inside a tuple → mutate the inner object explicitly
- Inside an expression → the walrus operator
Notes
Complexity
Mutation is usually O(len(b)); rebinding is O(len(a) + len(b))
Return
Nothing — it is a statement
CPython impl
Compiled to INPLACE_* opcodes, dispatching to __iadd__ and friends with a fallback to __add__
Memory
In-place forms avoid allocating; rebinding forms allocate a new object
Thread-safe
No — read-modify-write is not atomic, so counters need a lock
FAQ
Because lists implement __iadd__, which extends the existing list rather than creating a new one. Both names still point at that same list, so both see the new item. a = a + [2] creates a new list instead and leaves the original alone.
a = [1] b = a a += [2] b # [1, 2]
History
2.0
Augmented assignment added by PEP 203, with the __iop__ protocol.
3.5
@= added alongside the matrix multiplication operator.