Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

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.