Post
The modulo operator % in Python
What % does in Python and where it's useful: even or odd, every Nth item, wrapping around a list or a clock, splitting seconds with divmod, digits, FizzBuzz, and how it treats negative numbers.
% is the modulo operator: the remainder after division. The number operations post introduced it in a few lines.
Every example ran with Python 3.13, checked against the binary arithmetic operations reference and the divmod() docs.
What % does
a % b is what’s left over when you divide a into groups of b:
print(17 % 5, 10 % 2, 3 % 7, 0 % 4)
2 0 3 0
17 is three groups of 5 plus 2. 3 is smaller than 7, so all of it is left over.
% goes hand in hand with floor division //. The docs give the identity x == (x//y)*y + (x%y): the quotient times the divisor, plus the remainder, gives back the original number.
Even or odd
A number is even when dividing it by 2 leaves nothing:
number = 7
if number % 2 == 0:
print(number, "is even")
else:
print(number, "is odd")
7 is odd
The same idea works for any divisor. n % 3 == 0 means “n divides by 3”.
Every Nth item
Inside a loop, % picks every Nth step:
for i in range(1, 11):
if i % 3 == 0:
print(i, end=" ")
print()
3 6 9
In real code this is “save every 100 rows” or “print progress every 1,000 items”: if i % 1000 == 0:.
Wrapping around
The result of x % n is always between 0 and n - 1 (for positive n). That makes it perfect for going around in a circle, like days of the week:
days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
today = 5
print(days[(today + 3) % 7])
print(days[(today - 8) % 7])
Mon
Thu
Today is index 5 (Friday). Three days later is index 8, and 8 % 7 is 1, Monday. Eight days back is index -3, and -3 % 7 is 4, Thursday. The index never falls off the list. A 24-hour clock works the same way.
%= keeps an angle between 0 and 359:
angle = 350
angle += 20
angle %= 360
print(angle)
10
Splitting units with divmod
To turn seconds into hours, minutes and seconds, you need both the quotient and the remainder. divmod(x, y) returns both as a pair, (x // y, x % y):
total_seconds = 3725
minutes, seconds = divmod(total_seconds, 60)
hours, minutes = divmod(minutes, 60)
print(f"{hours}:{minutes:02}:{seconds:02}")
1:02:05
The :02 pads with a zero to two digits, as shown in the f-strings post.
Digits of a number
% 10 gives the last digit of a whole number. % 100 gives the last two. Combine it with // to get any digit:
n = 4096
print(n % 10, n % 100, n // 10 % 10)
6 96 9
FizzBuzz
The interview exercise tests % and elif order: print the numbers from 1 to 15, but “Fizz” for multiples of 3, “Buzz” for multiples of 5, and “FizzBuzz” for multiples of both:
for n in range(1, 16):
if n % 15 == 0:
print("FizzBuzz")
elif n % 3 == 0:
print("Fizz")
elif n % 5 == 0:
print("Buzz")
else:
print(n)
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
(Output shown on one line to save space.) The n % 15 check must come first, or 15 would match n % 3 == 0 and print “Fizz”, as explained in the if/elif post.
Negative numbers
Here Python differs from C, Java and JavaScript. The docs say the result of % “always yields a result with the same sign as its second operand (or zero)”:
print(-7 % 3, 7 % -3, -7 % -3)
print(-7 // 3, -7 == (-7 // 3) * 3 + (-7 % 3))
import math
print(math.fmod(-7, 3))
2 -2 -1
-3 True
-1.0
-7 // 3 rounds down to -3, and -3 * 3 + 2 is -7, so the remainder is 2. In C or JavaScript, -7 % 3 is -1. math.fmod() gives that behavior, and the math docs recommend fmod() for floats and % for integers.
Python’s rule is the reason the wrap-around trick works with negative indexes, and why n % 2 == 1 is a correct odd check even for negative n:
print(-3 % 2 == 1, -3 % 2)
True 1
In C, -3 % 2 is -1, so the same check would fail.
Floats
% works on floats too, but float rounding can show up in the result:
print(7.5 % 2, 5 % 0.1)
1.5 0.09999999999999973
5 % 0.1 isn’t 0 because 0.1 can’t be stored exactly. Use % with whole numbers when you can.
Common mistakes
- Dividing by zero.
5 % 0raisesZeroDivisionError: integer modulo by zero, the same as5 / 0. - Confusing
%with percent.50 % 10is 0, not “5”. For a percentage, multiply:part / total * 100. - Mistaking
%in strings for modulo."%s is %d years old" % ("Dana", 30)is the old string formatting. With a string on the left,%formats text; use f-strings instead.
What’s next
Next in the series: loops with for and while, where % and conditions do most of their work.