Advanced Uses and Overriding .__mod__
00:00
In the previous lesson, I showed you some example uses of the %
operator. In this lesson, I’m going to take you past int
and float
and show you how to use the %
operator with different classes, including your own.
00:14
In an earlier lesson, I mentioned the imprecise nature of floats. Here’s an example. What should be exactly 13.2
ends up with some crufty stuff at the end.
00:31
The capital-d Decimal
class supports a fixed precision for float-like operations.
00:41
You can create a Decimal
object by passing it a string representation of the number that you’re after. You can then do multiplication on that. A Decimal
object times an integer or times a float ends up in another Decimal
object.
00:59
Notice that the resulting value here doesn’t have the precision problem that float
does. You can even specify how much precision the Decimal
class is supposed to use.
01:19
The .prec
(precision) value of the context of the decimal
module specifies how many digits that Decimal
maintains inside of its calculation.
01:28 Be aware that this is the total number of digits, both to the left and to the right of the decimal point.
01:38 1 divided by 7, normally infinitely repeating, is succinctly kept in just six decimal places. The zero to the left of the decimal point doesn’t count in the total.
01:50
You can do modular arithmetic with the Decimal
class.
01:57 So far, no different than integers.
02:04
But here’s a case where the precision is correct. You no longer get the inaccuracy that’s introduced when you do this same calculation with floats. Decimal
objects also support negative mods.
02:23
But, as you might recall, there are different ways of calculating negative modulus. The %
operator uses the floor()
method, the Decimal
class uses the trunc()
method, so results from Decimal
will be closer to math
library’s fmod()
function.
02:48
Here’s an example with both the float imprecision and the negative result using the %
operator.
02:59
And there it is again using the Decimal
class. The extra inaccuracy introduced by float
is gone, and this uses the trunc()
division method, so you get different results depending on whether you’re using ints, floats, or decimals. Everything in Python is an object—the ints, the floats, and, of course, the Decimal
objects. Operators on those objects map to methods inside of the objects.
03:26
These methods are referred to as dunder methods, dunder being short for double underscore (__
). The %
operator calls .__mod__()
.
03:37
If you look at the source code of the decimal
library that I just demonstrated, when the %
operator is called, it gets mapped to Decimal.__mod__()
.
03:48
This isn’t just a weird little implementation detail—this is also something you can take advantage of. If you want your own classes to support the %
operator, you can do that by implementing the .__mod__()
method.
04:03
Here I’m going to show you a class that represents a series of integers. Inside of it, I’m going to implement the .__mod__()
method so that I can do mod math on all of the integers in the series. Let me just scroll down.
04:20
First off, in the constructor, it creates a member called .contents
where it stores what was passed in. The arguments to the constructor are looped through and appended to self.contents
and converted to int
.
04:34
I’ve done this so that if somebody passes in a non-integer value to *args
, line 7 will either convert it or force an exception to be raised.
04:45
.__mod__()
isn’t the only operator that can be done on this class. Lines 9 and 10 implement .__len__()
. This is what gets called when len()
(length) is called on the object.
04:57
In this case, I’m reporting the length of the internal array as the length of the int list. Implementing .__iter__()
provides an iterator for the class. This way, the class can be looped over.
05:11
I’m just mapping this to an iterator of the internal representation of the .contents
. With me so far? Now here’s how to actually implement .__mod__()
.
05:24
I’m supporting two different use cases. The first use case is when the object of the class encounters %
and then an integer. In this case, I’m doing a list comprehension in line 17, calculating the mod against each one of the items inside of the .contents
array, and then I’m creating a new IntList
object consisting of the old int
object’s .contents
modded with that single integer.
05:54
The second use case is to mod two IntList
objects together. To keep it simple, I’m only going to support the case where the two IntList
objects are of the same length. Line 20 checks whether or not a mod is being done against another IntList
and double-checks that if it is, that the two lengths are the same.
06:16
The len()
operator on the IntList
objects here will end up calling .__len__()
on line 9. I enumerate through the array of items inside of this object and I perform mod against the equivalently indexed value in the other IntList
.
06:36
All of this is stored inside of a list. And then on line 25, a new IntList
object is created with the results. Line 27 is a safety. If you get here, then the mod operation either wasn’t against an int
or wasn’t against an IntList
of the same length, so an error should be raised.
07:04 And here it is in practice.
07:10
I’ve created a new IntList
containing the numbers 1
, 5
, 15
, and 18
. I can do len()
, which results in 4
because of the four integers inside of it.
07:24
And because of the .__iter__()
method being implemented, I can convert this value to a list
. The list
class iterates through the IntList
and returns a list of the values.
07:37
Doing foo % 12
causes % 12
to happen for each item in the list. Converting that IntList
that returns into a list presents the following result. 1 % 12
is 1
, 5 % 12
is 5
, 15 % 12
is 3
, and 18 % 12
is 6
.
08:05
Here, I’m creating another IntList
. Again with four values inside of it, this time 12
, 6
, 12
, and 6
.
08:17
Modding foo
with bar
results in a new IntList
. Converting that into a list
shows you the results. 1 % 12
is 1
, 5 % 6
is 5
, 15 % 12
is 3
, and 18 % 6
is 0
.
08:38
And finally, to show you the error condition because I didn’t support a case with floats when I attempt to do IntList
mod 3.5
, the AttributeError
gets raised.
08:51 And now you’re able to use the mod operator with any class that you implement. Next up, I’ll summarize the course.
Become a Member to join the conversation.