8.4. Operator Right
x + y
- if method "add" on objectx
fails, then call "radd" on objecty
(y.__radd__(x)
)x - y
- if method "sub" on objectx
fails, then call "rsub" on objecty
(y.__rsub__(x)
)x * y
- if method "mul" on objectx
fails, then call "rmul" on objecty
(y.__rmul__(x)
)x ** y
- if method "pow" on objectx
fails, then call "rpow" on objecty
(y.__rpow__(x)
)x @ y
- if method "matmul" on objectx
fails, then call "rmatmul" on objecty
(y.__rmatmul__(x)
)x / y
- if method "truediv" on objectx
fails, then call "rtruediv" on objecty
(y.__rtruediv__(x)
)x // y
- if method "floordiv" on objectx
fails, then call "rfloordiv" on objecty
(y.__rfloordiv__(x)
)x % y
- if method "mod" on objectx
fails, then call "rmod" on objecty
(y.__rmod__(x)
)
Operator |
Method |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8.4.1. Syntax
>>> class MyClass:
... def __radd__(self, other): ... # when a + b raise exception
... def __rsub__(self, other): ... # when a - b raise exception
... def __rmul__(self, other): ... # when a * b raise exception
... def __rpow__(self, power, modulo=None): ... # when a ** b raise exception
... def __rmatmul__(self, other): ... # when a @ b raise exception
... def __rtruediv__(self, other): ... # when a / b raise exception
... def __rfloordiv__(self, other): ... # when a // b raise exception
... def __rmod__(self, other): ... # when a % b raise exception
8.4.2. Recap
>>> 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)
>>>
>>> a + b
Vector(x=4, y=6)
8.4.3. Left Operation
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})'
...
... def __add__(self, other):
... return Vector(x=self.x+other.x, y=self.y+other.y)
>>>
>>>
>>> a = Vector(x=1, y=2)
>>> b = (3, 4)
>>>
>>> a + b
Traceback (most recent call last):
AttributeError: 'tuple' object has no attribute 'x'
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 __add__(self, other):
... if isinstance(other, tuple):
... other = Vector(*other)
... return Vector(x=self.x+other.x, y=self.y+other.y)
>>>
>>>
>>> a = Vector(x=1, y=2)
>>> b = (3, 4)
>>>
>>> a + b
Vector(x=4, y=6)
>>>
>>> a.__add__(b)
Vector(x=4, y=6)
8.4.4. Right Operation
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})'
...
... def __add__(self, other):
... if isinstance(other, tuple):
... other = Vector(*other)
... return Vector(x=self.x+other.x, y=self.y+other.y)
>>>
>>>
>>> a = (1, 2)
>>> b = Vector(x=3, y=4)
>>>
>>> a + b
Traceback (most recent call last):
TypeError: can only concatenate tuple (not "Vector") to tuple
>>>
>>> a.__add__(b)
Traceback (most recent call last):
TypeError: can only concatenate tuple (not "Vector") to tuple
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 __add__(self, other):
... if isinstance(other, tuple):
... other = Vector(*other)
... return Vector(x=self.x+other.x, y=self.y+other.y)
...
... def __radd__(self, other):
... return self.__add__(other)
>>>
>>>
>>> a = (1, 2)
>>> b = Vector(x=3, y=4)
>>>
>>> a + b
Vector(x=4, y=6)
>>>
>>> a.__add__(b)
Traceback (most recent call last):
TypeError: can only concatenate tuple (not "Vector") to tuple
>>>
>>> b.__radd__(a)
Vector(x=4, y=6)
8.4.5. Case Study
a = list([1, 2])
b = list([3, 4])
# %%
a + b
# [1, 2, 3, 4]
a.__add__(b)
# [1, 2, 3, 4]
# %%
type(a)
# <class 'list'>
type(b)
# <class 'list'>
# %%
class list:
def __add__(self, other):
...
import numpy as np
a = np.array([1, 2])
b = np.array([3, 4])
# %%
a + b
# array([4, 6])
a.__add__(b)
# array([4, 6])
# %%
type(a)
# <class 'numpy.ndarray'>
type(b)
# <class 'numpy.ndarray'>
# %%
class ndarray:
def __add__(self, other):
...
import numpy as np
a = np.array([1, 2])
b = list([3, 4])
# %%
a + b
# array([4, 6])
a.__add__(b)
# array([4, 6])
# %%
type(a)
# <class 'numpy.ndarray'>
type(b)
# <class 'list'>
# %%
class ndarray:
def __add__(self, other):
if isinstance(other, list):
other = np.array(other)
...
import numpy as np
a = list([1, 2])
b = np.array([3, 4])
# %%
a + b
# array([4, 6])
a.__add__(b)
# TypeError: can only concatenate list (not "numpy.ndarray") to list
# %%
b.__radd__(a)
# array([4, 6])
# %%
type(a)
# <class 'list'>
type(b)
# <class 'numpy.ndarray'>
# %%
class ndarray:
def __radd__(self, other):
return self + other
def __add__(self, other):
if isinstance(other, list):
other = np.array(other)
...
8.4.6. Use Case
>>> import numpy as np
>>>
>>>
>>> mylist = [1, 2, 3]
>>> myarr = np.array([4,5,6])
>>>
>>>
>>> myarr + mylist
array([5, 7, 9])
>>>
>>>
>>> mylist + myarr
array([5, 7, 9])
>>>
>>>
>>> mylist.__add__(myarr)
Traceback (most recent call last):
TypeError: can only concatenate list (not "numpy.ndarray") to list
>>>
>>> myarr.__radd__(mylist)
array([5, 7, 9])
>>> class ndarray:
... def __add__(self, other):
... if isinstance(other, list):
... other = np.array(other)
... if isinstance(other, np.array):
... ...
...
... def __radd__(self, other):
... if isinstance(other, list):
... other = np.array(other)
... if isinstance(other, np.array):
... ...
8.4.7. Use Case - 1
Game
>>> hero @ Position(x=50, y=120)
>>>
>>> hero['gold'] += dragon['gold']
8.4.8. Use Case - 2
>>> from dataclasses import dataclass, field
>>>
>>>
>>> @dataclass
... class User:
... firstname: str
... lastname: str
>>>
>>>
>>> @dataclass
... class Group:
... members: list[User] = field(default_factory=list)
...
... def __iadd__(self, other):
... self.members.append(other)
... return self
>>>
>>>
>>> ares3 = Group()
>>> ares3 += User('Mark', 'Watney')
>>> ares3 += User('Melissa', 'Lewis')
>>>
>>> print(ares3)
Group(members=[User(firstname='Mark', lastname='Watney'), User(firstname='Melissa', lastname='Lewis')])
>>>
>>> for member in ares3.members:
... print(member)
User(firstname='Mark', lastname='Watney')
User(firstname='Melissa', lastname='Lewis')
8.4.9. Use Case - 3
SetUp:
>>> import numpy as np
Example 1:
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>>
>>> a + b
[1, 2, 3, 4, 5, 6]
>>>
>>> a.__add__(b)
[1, 2, 3, 4, 5, 6]
Example 2:
>>> a = np.array([1, 2, 3])
>>> b = np.array([4, 5, 6])
>>>
>>> a + b
array([5, 7, 9])
>>>
>>> a.__add__(b)
array([5, 7, 9])
Example 3:
>>> a = np.array([1, 2, 3])
>>> b = [4, 5, 6]
>>>
>>> a + b
array([5, 7, 9])
>>>
>>> a.__add__(b)
array([5, 7, 9])
Why this works:
>>> class ndarray:
... def __add__(self, other):
... if type(other) is not ndarray:
... other = np.array(other)
Example 4:
>>> a = [1, 2, 3]
>>> b = np.array([4, 5, 6])
>>>
>>> a + b
array([5, 7, 9])
>>>
>>> a.__add__(b)
Traceback (most recent call last):
TypeError: can only concatenate list (not "numpy.ndarray") to list
>>>
>>> b.__radd__(a)
array([5, 7, 9])
Why this works:
>>> class ndarray:
... def __add__(self, other):
... if type(other) is not ndarray:
... other = np.array(other)
...
... def __radd__(self, other):
... if type(other) is not ndarray:
... other = np.array(other)