Arithmetical and logical operations

In Python, when we add two numbers, what is happening under the hood is that one of them is calling its method on the other.

Consider the following example:

>>> 1 + 2
3

Here, under the hood, 1 is calling its __add__ method on 2. Inside, 1 is checking whether 2 is of a supported type, and runs the computation.

We don't want to mess with number behavior, but this process is exactly the same for any custom class as well. In other words, if you define the __add__ function of your class, you'll be able to add something to this class. In some cases, you might try to add something that your class doesn't know how to handle. It should then raise a built-in notImplemented exception. In this case, Python will try to run the __radd__ method of this second instance (which stands for right add, of course), and, if it won't work, it raises an exception.

Take a look at this snippet. Here, we define two classes—one for Fish and another one for a School. The School class is expecting to get multiple fish. The Fish class has an __add__ method that returns School of the two fish:

class School:
def __init__(self, *fishes):
self.fishes = list(fishes)

class Fish:

def __add__(self, other):
return School(self, other)

Let's see how it works:

>>> F1, F2 = Fish(), Fish()
>>> F1 + F2
<__main__.School at 0x104efdd68>

As you can see, the sum of two fish is now presented as a School object. Of course, this feature is not limited to addition. Take a look at the following table, which covers quite a few operations, both arithmetical and logical:

Basic function

"Right side" execution (if the element on the left does not support operation, method of the element on the right is used) Corresponding symbol and arithmetical meaning
object.__add__(self, other) object.__radd__(self, other) +, addition
object.__sub__(self, other) object.__rsub__(self, other) -, subtraction
object.__mul__(self, other) object.__rmul__(self, other) *, multiplication
object.__matmul__(self, other) object.__rmatmul__(self, other) @, matrix multiplication
object.__truediv__(self, other) object.__rtruediv__(self, other) /, division
object.__floordiv__(self, other) object.__rfloordiv__(self, other) //, floor division
object.__mod__(self, other) object.__rmod__(self, other) %, modulo
object.__divmod__(self, other) object.__rdivmod__(self, other) divmod(), for numbers, returns the quotient and remainder
object.__pow__(self, other[, modulo]) object.__rpow__(self, other[, modulo]) **, power
object.__and__(self, other) object.__rand__(self, other) &, AND
object.__or__(self, other) object.__ror__(self, other) |, OR
object.__xor__(self, other) object.__rxor__(self, other) ^, XOR

 

But where and when shall we use them? Wherever it feels natural. For example, a built-in library called pathlib uses division to concatenate two paths. It makes sense, because we use the slash symbol to add paths. Take a look at how it works:

>>> from pathlib import Path

>>> path = Path('.').parent / 'data'
>>> path.absolute()
PosixPath('/Users/philippk/Dropbox/personal_projects/Packt_book/Chapter08/data')
The preceding code works fine for Notebooks. Since scripts could be called from anywhere, they have a path to the file stored in the __file__ variable. Simply replace the dot with this variable and you're good to go.

The resulting path, in this case, will represent the relative path to the data folder, located next to the running script, and does not depend on the absolute location of both.

Another good example is how the visualization package Altair uses these operations. For Altair, the addition of two plots will overlay them, which seems intuitive (although it breaks the rule of symmetry—here, the order of charts does matter). Similarly, using a pipe (|) symbol (or) will put two charts side by side.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.2.133