8.3. Operator Increment

  • x += y - will call method "iadd" on object x (x.__iadd__(y))

  • x -= y - will call method "isub" on object x (x.__isub__(y))

  • x *= y - will call method "imul" on object x (x.__imul__(y))

  • x **= y - will call method "ipow" on object x (x.__ipow__(y))

  • x @= y - will call method "imatmul" on object x (x.__imatmul__(y))

  • x /= y - will call method "itruediv" on object x (x.__itruediv__(y))

  • x //= y - will call method "ifloordiv" on object x (x.__ifloordiv__(y))

  • x %= y - will call method "imod" on object x (x.__imod__(y))

Table 8.2. Numerical Operator Overload

Operator

Method

obj += other

obj.__iadd__(other)

obj -= other

obj.__isub__(other)

obj *= other

obj.__imul__(other)

obj **= other

obj.__ipow__(other)

obj @= other

obj.__imatmul__(other)

obj /= other

obj.__itruediv__(other)

obj //= other

obj.__ifloordiv__(other)

obj %= other

obj.__imod__(other)

8.3.1. Syntax

>>> class MyClass:
...     def __iadd__(self, other): ...               # a += b
...     def __isub__(self, other): ...               # a -= b
...     def __imul__(self, other): ...               # a *= b
...     def __ipow__(self, power, modulo=None): ...  # a **= b
...     def __imatmul__(self, other): ...            # a @= b
...     def __itruediv__(self, other): ...           # a /= b
...     def __ifloordiv__(self, other): ...          # a //= b
...     def __imod__(self, other): ...               # a %= b

8.3.2. Problem

>>> class Vector:
...     x: int
...     y: int
...
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...
...     def __repr__(self):
...         return f'Vector(x={self.x}, y={self.y})'
>>>
>>>
>>> a = Vector(x=1, y=2)
>>> b = Vector(x=3, y=4)
>>>
>>> a += b
Traceback (most recent call last):
TypeError: unsupported operand type(s) for +=: 'Vector' and 'Vector'

8.3.3. Solution

>>> class Vector:
...     x: int
...     y: int
...
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...
...     def __repr__(self):
...         return f'Vector(x={self.x}, y={self.y})'
...
...     def __iadd__(self, other):
...         self.x += other.x
...         self.y += other.y
...         return self
>>>
>>>
>>> a = Vector(x=1, y=2)
>>> b = Vector(x=3, y=4)
>>> a += b
>>>
>>> print(a)
Vector(x=4, y=6)

8.3.4. Add vs. Iadd

>>> class Vector:
...     x: int
...     y: int
...
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...
...     def __repr__(self):
...         return f'Vector(x={self.x}, y={self.y})'
...
...     def __add__(self, other):
...         return Vector(x=self.x + other.x,
...                       y=self.y + other.y)
>>>
>>>
>>> a = Vector(x=1, y=2)
>>> b = Vector(x=3, y=4)
>>>
>>> id(a)  
4387892080
>>>
>>> a += b
>>>
>>> id(a)  
4387901360
>>>
>>> print(a)
Vector(x=4, y=6)
>>> class Vector:
...     x: int
...     y: int
...
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...
...     def __repr__(self):
...         return f'Vector(x={self.x}, y={self.y})'
...
...     def __iadd__(self, other):
...         self.x += other.x
...         self.y += other.y
...         return self
>>>
>>>
>>> a = Vector(x=1, y=2)
>>> b = Vector(x=3, y=4)
>>>
>>> id(a)  
4387904192
>>>
>>> a += b
>>>
>>> id(a)  
4387904192
>>>
>>> print(a)
Vector(x=4, y=6)

8.3.5. Returning Self

  • Always return self from increment methods

  • Python will assign to the left-hand side variable the result of the increment method

>>> class Vector:
...     x: int
...     y: int
...
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...
...     def __repr__(self):
...         return f'Vector(x={self.x}, y={self.y})'
...
...     def __iadd__(self, other):
...         self.x += other.x
...         self.y += other.y
>>>
>>>
>>> a = Vector(x=1, y=2)
>>> b = Vector(x=3, y=4)
>>>
>>> a += b
>>>
>>> print(a)
None
>>>
>>> a is None
True

8.3.6. Use Case - 1

>>> hero['gold'] += dragon['gold']  

8.3.7. Use Case - 2

>>> class Group:
...     def __init__(self):
...         self.members = []
...
...     def __iadd__(self, other):
...         self.members.append(other)
...         return self
>>>
>>>
>>> admins = Group()
>>> admins += 'mwatney'
>>> admins += 'mlewis'
>>> admins += 'rmartinez'
>>>
>>> admins.members
['mwatney', 'mlewis', 'rmartinez']

8.3.8. Assignments

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Operator Increment Add
# - Difficulty: easy
# - Lines: 3
# - Minutes: 3

# %% English
# 1. Overload operator `+=`
# 2. Make `User` objects able to add `Groups`, for example:
#    - `mark = User(firstname='Mark', lastname='Watney')`
#    - `mark += Group(1, 'admins')`
#    - `mark += Group(2, 'staff')`
# 3. Run doctests - all must succeed

# %% Polish
# 1. Przeciąż operator `+=`
# 2. Spraw aby do obiektów klasy `User` można dodać `Group`, przykład:
#    - `mark = User(firstname='Mark', lastname='Watney')`
#    - `mark += Group(1, 'admins')`
#    - `mark += Group(2, 'staff')`
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `object.__iadd__() -> self`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from pprint import pprint

>>> mark = User(firstname='Mark', lastname='Watney', groups=[])
>>> mark += Group(1, 'admins')
>>> mark += Group(2, 'staff')

>>> pprint(mark, width=50)
User(firstname='Mark',
     lastname='Watney',
     groups=[Group(gid=1, name='admins'),
             Group(gid=2, name='staff')])
"""

from dataclasses import dataclass


@dataclass
class Group:
    gid: int
    name: str

# Make `User` objects able to add `Groups`, for example:
# - `mark = User(firstname='Mark', lastname='Watney')`
# - `mark += Group(1, 'admins')`
# - `mark += Group(2, 'staff')`
# type: type[User]
@dataclass
class User:
    firstname: str
    lastname: str
    groups: list[Group]