8.1. Operator Arithmetic
Left Operators
Right Operators
In-Place Operators
8.1.1. SetUp
>>> from dataclasses import dataclass
8.1.2. Numeric
-obj
->obj.__neg__()
+obj
->obj.__pos__()
Example: -1 or +1
>>> @dataclass
... class Vector:
... x: int
... y: int
...
... def __neg__(self): ... # -obj
... def __pos__(self): ... # +obj
8.1.3. Left Operators
obj + other
->obj.__add__(other)
obj - other
->obj.__sub__(other)
obj * other
->obj.__mul__(other)
obj ** other
->obj.__pow__(other)
obj @ other
->obj.__matmul__(other)
obj / other
->obj.__truediv__(other)
obj // other
->obj.__floordiv__(other)
obj % other
->obj.__mod__(other)
>>> @dataclass
... class Vector:
... x: int
... y: int
...
... def __add__(self, other):
... x = self.x + other.x
... y = self.y + other.y
... return Vector(x, y)
>>>
>>> a = Vector(1, 2)
>>> b = Vector(3, 4)
>>>
>>> a + b
Vector(x=4, y=6)
>>> @dataclass
... class Vector:
... x: int
... y: int
...
... def __sub__(self, other):
... x = self.x - other.x
... y = self.y - other.y
... return Vector(x, y)
>>>
>>> a = Vector(1, 2)
>>> b = Vector(3, 4)
>>>
>>> a - b
Vector(x=-2, y=-2)
8.1.4. In-Place Operators
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)
>>> @dataclass
... class Vector:
... x: int
... y: int
...
... def __iadd__(self, other):
... self.x = other.x
... self.y = other.y
... return self
>>>
>>> a = Vector(1, 2)
>>> b = Vector(3, 4)
>>>
>>> a += b
Vector(x=4, y=6)
In-Place operators will update object in-place, so the id (memory address) of the object will not change.
>>> a = Vector(1, 2)
>>> b = Vector(3, 4)
>>>
>>> id(a)
4789628672
>>>
>>> a += b
>>> a
Vector(x=4, y=6)
>>>
>>> id(a)
4789628672
In case of absence of __iadd__
method,
Python will use __add__
method. Note, that
__add__
method will return new object, so the
id of the object will change.
>>> @dataclass
... class Vector:
... x: int
... y: int
...
... def __add__(self, other):
... x = self.x + other.x
... y = self.y + other.y
... return Vector(x, y)
>>> a = Vector(1,2)
>>> b = Vector(2,3)
>>>
>>> id(a)
4789628672
>>>
>>> a += b
>>> a
Vector(x=4, y=6)
>>>
>>> id(a)
4789628672
8.1.5. Right Operators
obj + other
- whenobj.__add__(other)
fail ->other.__radd__(obj)
obj - other
- whenobj.__sub__(other)
fail ->other.__rsub__(obj)
obj * other
- whenobj.__mul__(other)
fail ->other.__rmul__(obj)
obj ** other
- whenobj.__pow__(other)
fail ->other.__rpow__(obj)
obj @ other
- whenobj.__matmul__(other)
fail ->other.__rmatmul__(obj)
obj / other
- whenobj.__truediv__(other)
fail ->other.__rtruediv__(obj)
obj // other
- whenobj.__floordiv__(other)
fail ->other.__rfloordiv__(obj)
obj % other
- whenobj.__mod__(other)
fail ->other.__rmod__(obj)
8.1.6. Use Case - 0x01
>>> import numpy as np
>>>
>>> a = np.array([1, 2, 3])
>>> b = np.array([4, 5, 6])
>>> type(a)
<class 'numpy.ndarray'>
>>>
>>> type(b)
<class 'numpy.ndarray'>
>>> a + b
array([5, 7, 9])
>>>
>>> a.__add__(b)
array([5, 7, 9])
Intuitive implementation:
>>> class ndarray:
... def __add__(self, other):
... ...
8.1.7. Use Case - 0x02
>>> import numpy as np
>>>
>>> a = np.array([1, 2, 3])
>>> b = [4, 5, 6]
>>> type(a)
<class 'numpy.ndarray'>
>>>
>>> type(b)
<class 'list'>
>>> a + b
array([5, 7, 9])
>>>
>>> a.__add__(b)
array([5, 7, 9])
Intuitive implementation:
>>> class ndarray:
... def __add__(self, other):
... if type(other) is list:
... other = np.array(other)
... ...
8.1.8. Use Case - 0x03
>>> import numpy as np
>>>
>>> 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])
a + b
will calla.__add__(b)
-> if value, then return result
-> if exception, then call
b.__radd__(a)
Intuitive implementation:
>>> class ndarray:
... def __add__(self, other):
... if type(other) is list:
... other = np.array(other)
... ...
...
... def __radd__(self, other):
... if type(other) is list:
... other = np.array(other)
... ...
8.1.9. Use Case - 0x04
List:
>>> data = [1,2,3]
>>> data += [4,5,6]
>>>
>>> data
[1, 2, 3, 4, 5, 6]
Tuple:
>>> data = (1,2,3)
>>> data += (4,5,6)
>>>
>>> data
(1, 2, 3, 4, 5, 6)
Intuitive implementation:
>>> class list:
... def __iadd__(self, other):
... self.extend(other)
...
>>> class tuple:
... def __iadd__(self, other):
... return self + other
8.1.10. Use Case - 0x05
>>> 10 % 8
2
>>>
>>> '10' % 8
TypeError: not all arguments converted during string formatting
>>>
>>> '%s' % 8
'8'
>>>
>>> '10%s' % 8
'108'
Intuitive implementation:
>>> class int:
... def __mod__(self, other):
... # modulo division
...
>>> class str:
... def __mod__(self, other):
... # string formatting
Python 0.x, 1.x and 2.x (and still available in 3.x):
>>> firstname = 'Mark'
>>> lastname = 'Watney'
>>>
>>> x = 'Hello %s' % firstname
>>> x = 'Hello %s %s' % (firstname, lastname)
>>> x = 'Hello %(fname)s %(lname)s' % {'fname': firstname, 'lname': lastname}
Python from 3.0 until 3.6:
>>> firstname = 'Mark'
>>> lastname = 'Watney'
>>>
>>> x = 'Hello {} {}'.format(firstname, lastname)
>>> x = 'Hello {0} {1}'.format(firstname, lastname)
>>> x = 'Hello {1} {0}'.format(firstname, lastname)
>>> x = 'Hello {fname} {lname}'.format(fname=firstname, lname=lastname)
Python from 3.6 and above:
>>> firstname = 'Mark'
>>> lastname = 'Watney'
>>>
>>> x = f'Hello {firstname} {lastname}'