PHP week PHP week
This week up to 80% off on PHP courses. More info

Lesson 12 - Magic Methods in Python - Math methods

In the previous lesson, Magic Methods in Python, we introduced magic methods. In today's Python tutorial, we're going to continue with them. This time, we're going to show special methods for mathematical operators. We'll also show their use on the Vector class.

Standard operators

These operators are called automatically on an object a when used like this:

c = a + b

The object a is passed through the self parameter and the object b is the other parameter.

__add__(self, other)

The method is called on the first object when the + operator is used:

c = a + b

__sub__(self, other)

The method is called when subtracting by the - operator:

c = a - b

__mul__(self, other)

The method is called when multiplying by the * operator:

c = a * b

__truediv__(self, other)

The method is called when dividing by the / operator:

c = a / b

__floordiv__(self, other)

The method is called when dividing by the // operator (whole-number division):

c = a // b

__mod__(self, other)

The method is called when getting the remainder after the whole-number division by using the modulo operator %:

c = a % b

__divmod__(self, other)

Returns a pair (a // b, a % b) for integers:

c = divmod(a, b)

__pow__(self, other, modulo)

The method is executed when we use the power ** operator. The method should be able to take a third, optional (modulo) argument (https://docs.python.org/…nctions.html#pow):

c = a ** b

__lshift__(self, other)

Executed when using the left bit-shift operator <<:

c = a << b

__rshift__(self, other)

Executed when using the right bit-shift operator >>:

c = a >> b

__and__(self, other)

Executed when using the bitwise AND operator &:

c = a & b

__xor__(self, other)

Executed when using the bitwise XOR operator ^ (Non-Equivalence):

c = a ^ b

__or__(self, other)

Executed when using the bitwise OR operator |:

c = a | b

Reversed operators

Reversed operators are called on the second object when the first object doesn't implement the operator.

For example:

sth = 1 + my_object

If the __add__() magic method isn't supported by int, which is probably not, the __radd__() method is called on my_object.

These operators are called automatically on the object b, with object b being the self parameter and object a being the other parameter.

  • __radd__(self, other) - Adding
  • __rsub__(self, other) - Subtracting
  • __rmul__(self, other) - Multiplying
  • __rtruediv__(self, other) - True division
  • __rfloordiv__(self, other) - Whole-number division
  • __rmod__(self, other) - The remainder after whole-number division - modulo
  • __rdivmod__(self, other) - Returns a pair (a // b, a % b) for integers
  • __rpow__(self, other, modulo) - Power
  • __rlshift__(self, other) - Left bit-shift
  • __rrshift__(self, other) - Right bit-shift
  • __rand__(self, other) - Bitwise AND
  • __rxor__(self, other) - Bitwise XOR (Non-Equivalence)
  • __ror__(self, other) - Bitwise OR

In place operators

These operators allow us to use a shortened notation (in place). The parameters of the methods are self and other, but return a modified self. If there's no method, Python will try to emulate it using defined methods.

An example:

my_object += 1

Python calls the __iadd__() method. If it fails, it calls __add__() as follows:

temp = my_object + 1 # calls __add__()
my_object = temp
  • __iadd__(self, other) - Adding
  • __isub__(self, other) - Subtracting
  • __imul__(self, other) - Multiplication
  • __itruediv__(self, other) - True division
  • __ifloordiv__(self, other) - Whole-number division
  • __imod__(self, other) The remainder of whole-number division - modulo
  • __ipow__(self, other, modulo) - Power
  • __ilshift__(self, other) - Left bit-shift
  • __irshift__(self, other) Right bit-shift
  • __iand__(self, other) - Bitwise AND
  • __ixor__(self, other) - Bitwise XOR (Non-Equivalence)
  • __ior__(self, other) - Bitwise OR

Other magic methods

__neg__(self)

Unary minus:

-a

__pos__(self)

Unary plus:

+a

__abs__(self)

Absolute value implementing behavior for abs():

abs(a)

__invert__(self)

Unary inversion:

~a

__complex__(self)

Implements behavior for the complex() function:

complex(a)

__int__(self)

Implements behavior for int():

int(a)

__float__(self)

Implements behavior for float():

float(a)

__round__(self, n)

Implements behavior for round():

round(a)

__index__(self)

Python uses this method to convert numeric types to int, for example, when truncating or using the built-in bin(), hex(), and oct() functions. This method should return the same result as the __int__() magic method. It should also return an integer (int).

Method examples

Let's create a simple Vector class, holding its 2 x and y components. We'll implement some of the magic methods on it.

class Vector:

    def __init__(self, x, y):
        self.x = float(x)
        self.y = float(y)

    def __str__(self):
        return "({0.x}, {0.y})".format(self)

    ...

The beginning is probably clear. In the __str__() method, we get the x and y attributes from self.

def __add__(self, other):
    if isinstance(other, Vector):
        return Vector(self.x+other.x, self.y+other.y)
    elif issubclass(type(other), Sequence):
        if len(other) == 2:
            return Vector(self.x+other[0], self.y+other[1])
    raise NotImplemented

First we compare whether the second object is also a Vector. If so, we return the sum of the x and y components as a new Vector.

Further branching is more complex. Using the built-in issubclass() function, we check if the second object class is a subclass of Sequence of the collections.abc module. This allows us to use the len() function on the object to get the first and second elements from the object. Without worrying that the object wouldn't support it. If both branches fail and don't return anything, the NotImplemented exception is thrown.

def __mul__(self, other):
    if issubclass(type(other), Real):
        return Vector(self.x * other, self.y * other)
    raise NotImplemented

Here we check if other is a subclass of Real (real number) from the numbers module. Therefore, we can multiply the Vector by int or float and avoid unnecessary branching and checking of object types.

The classes in the numbers and collections.abc modules are actually abstract base classes. These are classes where we have to implement a certain object interface if we inherit them. But let's wait with this for the next lesson, Magic Methods in Python - Collections and Descriptors.


 

 

Activities (1)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!