 realpython.com Go back Open original

# Python Modulo in Practice: How to Use the % Operator – Real Python

Now that you’ve gone through the basics of the Python modulo operator, you’ll look at some examples of using it to solve real-world programming problems. At times, it can be hard to determine when to use the modulo operator in your code. The examples below will give you an idea of the many ways it can be used.

In this section, you’ll see how you can use the modulo operator to determine if a number is even or odd. Using the modulo operator with a modulus of `2`, you can check any number to see if it’s evenly divisible by `2`. If it is evenly divisible, then it’s an even number.

Take a look at `is_even()` which checks to see if the `num` parameter is even:

``````def is_even(num): return num % 2 == 0
``````

Here `num % 2` will equal `0` if `num` is even and `1` if `num` is odd. Checking against `0` will return a Boolean of `True` or `False` based on whether or not `num` is even.

Checking for odd numbers is quite similar. To check for an odd number, you invert the equality check:

``````def is_odd(num): return num % 2 != 0
``````

This function will return `True` if `num % 2` does not equal `0`, meaning that there’s a remainder proving `num` is an odd number. Now, you may be wondering if you could use the following function to determine if `num` is an odd number:

``````def is_odd(num): return num % 2 == 1
``````

The answer to this question is yes and no. Technically, this function will work with the way Python calculates modulo with integers. That said, you should avoid comparing the result of a modulo operation with `1` as not all modulo operations in Python will return the same remainder.

You can see why in the following examples:

>>>
``````>>> -3 % 2
1 >>> 3 % -2
-1
``````

In the second example, the remainder takes the sign of the negative divisor and returns `-1`. In this case, the Boolean check `3 % -2 == 1` would return `False`.

However, if you compare the modulo operation with `0`, then it doesn’t matter which operand is negative. The result will always be `True` when it’s an even number:

>>>
``````>>> -2 % 2
0 >>> 2 % -2
0
``````

If you stick to comparing a Python modulo operation with `0`, then you shouldn’t have any problems checking for even and odd numbers or any other multiples of a number in your code.

In the next section, you’ll take a look at how you can use the modulo operator with loops to control the flow of your program.

With the Python modulo operator, you can run code at specific intervals inside a loop. This is done by performing a modulo operation with the current index of the loop and a modulus. The modulus number determines how often the interval-specific code will run in the loop.

Here’s an example:

``````def split_names_into_rows(name_list, modulus=3): for index, name in enumerate(name_list, start=1): print(f"{name:-^15} ", end="") if index % modulus == 0: print() print()
``````

This code defines `split_names_into_rows()`, which takes two parameters. `name_list` is a list of names that should be split into rows. `modulus` sets a modulus for the operation, effectively determining how many names should be in each row. `split_names_into_rows()` will loop over `name_list` and start a new row after it hits the `modulus` value.

Before breaking down the function in more detail, take a look at it in action:

>>>
``````>>> names = ["Picard", "Riker", "Troi", "Crusher", "Worf", "Data", "La Forge"]
>>> split_names_into_rows(names)
----Picard----- -----Riker----- -----Troi------
----Crusher---- -----Worf------ -----Data------
---La Forge----
``````

As you can see, the list of names has been split into three rows, with a maximum of three names in each row. `modulus` defaults to `3`, but you can specify any number:

>>>
``````>>> split_names_into_rows(names, modulus=4)
----Picard----- -----Riker----- -----Troi------ ----Crusher----
-----Worf------ -----Data------ ---La Forge---- >>> split_names_into_rows(names, modulus=2)
----Picard----- -----Riker-----
-----Troi------ ----Crusher----
-----Worf------ -----Data------
---La Forge---- >>> split_names_into_rows(names, modulus=1)
----Picard-----
-----Riker-----
-----Troi------
----Crusher----
-----Worf------
-----Data------
---La Forge----
``````

Now that you’ve seen the code in action, you can break down what it’s doing. First, it uses `enumerate` to iterate over `name_list`, assigning the current item in the list to `name` and a count value to `index`. You can see that the optional `start` argument for `enumerate` is set to `1`. This means that the `index` count will start at `1` instead of `0`:

``````for index, name in enumerate(name_list, start=1):
``````

Next, inside the loop, the function calls `print()` to output `name` to the current row. The `end` parameter for `print()` is an empty string (`""`) so it won’t output a newline at the end of the string. An f-string is passed to `print()`, which uses the string output formatting syntax that Python provides:

``````print(f"{name:-^15} ", end="")
``````

Without getting into too much detail, the `:-^15` syntax tells `print()` to do the following:

• Output at least `15` characters, even if the string is shorter than 15 characters.
• Center align the string.
• Fill any space on the right or left of the string with the hyphen character (`-`).

Now that the name has been printed to the row, take a look at the main part of `split_names_into_rows()`:

``````if index % modulus == 0: print()
``````

This code takes the current iteration `index` and, using the modulo operator, compares it with `modulus`. If the result equals `0`, then it can run interval-specific code. In this case, the function calls `print()` to add a newline, which starts a new row.

The above code is only one example. Using the pattern `index % modulus == 0` allows you to run different code at specific intervals in your loops. In the next section, you’ll take this concept a bit further and look at cyclic iteration.

Cyclic iteration describes a type of iteration that will reset once it gets to a certain point. Generally, this type of iteration is used to restrict the index of the iteration to a certain range.

You can use the modulo operator to create cyclic iteration. Take a look at an example using the `turtle` library to draw a shape:

``````import turtle
import random def draw_with_cyclic_iteration(): colors = ["green", "cyan", "orange", "purple", "red", "yellow", "white"] turtle.bgcolor("gray8") # Hex: #333333 turtle.pendown() turtle.pencolor(random.choice(colors)) # First color is random i = 0 # Initial index while True: i = (i + 1) % 6 # Update the index turtle.pensize(i) # Set pensize to i turtle.forward(225) turtle.right(170) # Pick a random color if i == 0: turtle.pencolor(random.choice(colors))
``````

The above code uses an infinite loop to draw a repeating star shape. After every six iterations, it changes the color of the pen. The pen size increases with each iteration until `i` is reset back to `0`. If you run the code, then you should get something similar to this: The important parts of this code are highlighted below:

``````import turtle
import random def draw_with_cyclic_iteration(): colors = ["green", "cyan", "orange", "purple", "red", "yellow", "white"] turtle.bgcolor("gray8") # Hex: #333333 turtle.pendown() turtle.pencolor(random.choice(colors)) i = 0 # Initial index  while True:
i = (i + 1) % 6 # Update the index
turtle.pensize(i) # Set pensize to i
turtle.forward(225)
turtle.right(170)

# Pick a random color
if i == 0:
turtle.pencolor(random.choice(colors))
``````

Each time through the loop, `i` is updated based on the results of `(i + 1) % 6`. This new `i` value is used to increase the `.pensize` with each iteration. Once `i` reaches `5`, `(i + 1) % 6` will equal `0`, and `i` will reset back to `0`.

You can see the steps of the iteration below for more clarification:

``````i = 0 : (0 + 1) % 6 = 1
i = 1 : (1 + 1) % 6 = 2
i = 2 : (2 + 1) % 6 = 3
i = 3 : (3 + 1) % 6 = 4
i = 4 : (4 + 1) % 6 = 5
i = 5 : (5 + 1) % 6 = 0 # Reset
``````

When `i` is reset back to `0`, the `.pencolor` changes to a new random color as seen below:

``````if i == 0: turtle.pencolor(random.choice(colors))
``````

The code in this section uses `6` as the modulus, but you could set it to any number to adjust how many times the loop will iterate before resetting the value `i`.

In this section, you’ll look at how you can use the modulo operator to convert units. The following examples take smaller units and convert them into larger units without using decimals. The modulo operator is used to determine any remainder that may exist when the smaller unit isn’t evenly divisible by the larger unit.

In this first example, you’ll convert inches into feet. The modulo operator is used to get the remaining inches that don’t evenly divide into feet. The floor division operator (`//`) is used to get the total feet rounded down:

``````def convert_inches_to_feet(total_inches): inches = total_inches % 12 feet = total_inches // 12 print(f"{total_inches} inches = {feet} feet and {inches} inches")
``````

Here’s an example of the function in use:

>>>
``````>>> convert_inches_to_feet(450)
450 inches = 37 feet and 6 inches
``````

As you can see from the output, `450 % 12` returns `6`, which is the remaining inches that weren’t evenly divided into feet. The result of `450 // 12` is `37`, which is the total number of feet by which the inches were evenly divided.

You can take this a bit further in this next example. `convert_minutes_to_days()` takes an integer, `total_mins`, representing a number of minutes and outputs the period of time in days, hours, and minutes:

``````def convert_minutes_to_days(total_mins): days = total_mins // 1440 extra_minutes = total_mins % 1440 hours = extra_minutes // 60 minutes = extra_minutes % 60 print(f"{total_mins} = {days} days, {hours} hours, and {minutes} minutes")
``````

Breaking this down, you can see that the function does the following:

1. Determines the total number of evenly divisible days with `total_mins // 1440`, where `1440` is the number of minutes in a day
2. Calculates any `extra_minutes` left over with `total_mins % 1440`
3. Uses the `extra_minutes` to get the evenly divisible `hours` and any extra `minutes`

You can see how it works below:

>>>
``````>>> convert_minutes_to_days(1503)
1503 = 1 days, 1 hours, and 3 minutes >>> convert_minutes_to_days(3456)
3456 = 2 days, 9 hours, and 36 minutes >>> convert_minutes_to_days(35000)
35000 = 24 days, 7 hours, and 20 minutes
``````

While the above examples only deal with converting inches to feet and minutes to days, you could use any type of units with the modulo operator to convert a smaller unit into a larger unit.

Note: Both of the above examples could be modified to use `divmod()` to make the code more succinct. If you remember, `divmod()` returns a tuple containing the results of floor division and modulo using the supplied parameters.

Below, the floor division and modulo operators have been replaced with `divmod()`:

``````def convert_inches_to_feet_updated(total_inches):
feet, inches = divmod(total_inches, 12)
print(f"{total_inches} inches = {feet} feet and {inches} inches")
``````

As you can see, `divmod(total_inches, 12)` returns a tuple, which is unpacked into `feet` and `inches`.

If you try this updated function, then you’ll receive the same results as before:

>>>
``````>>> convert_inches_to_feet(450)
450 inches = 37 feet and 6 inches >>> convert_inches_to_feet_updated(450)
450 inches = 37 feet and 6 inches
``````

You receive the same outcome, but now the code is more concise. You could update `convert_minutes_to_days()` as well:

``````def convert_minutes_to_days_updated(total_mins):
days, extra_minutes = divmod(total_mins, 1440)
hours, minutes = divmod(extra_minutes, 60)
print(f"{total_mins} = {days} days, {hours} hours, and {minutes} minutes")
``````

Using `divmod()`, the function is easier to read than the previous version and returns the same result:

>>>
``````>>> convert_minutes_to_days(1503)
1503 = 1 days, 1 hours, and 3 minutes >>> convert_minutes_to_days_updated(1503)
1503 = 1 days, 1 hours, and 3 minutes
``````

Using `divmod()` isn’t necessary for all situations, but it makes sense here as the unit conversion calculations use both floor division and modulo.

Now that you’ve seen how to use the modulo operator to convert units, in the next section you’ll look at how you can use the modulo operator to check for prime numbers.

In this next example, you’ll take a look at how you can use the Python modulo operator to check whether a number is a prime number. A prime number is any number that contains only two factors, `1` and itself. Some examples of prime numbers are `2`, `3`, `5`, `7`, `23`, `29`, `59`, `83`, and `97`.

The code below is an implementation for determining the primality of a number using the modulo operator:

``````def check_prime_number(num): if num < 2: print(f"{num} must be greater than or equal to 2 to be prime.") return factors = [(1, num)] i = 2 while i * i <= num: if num % i == 0: factors.append((i, num//i)) i += 1 if len(factors) > 1: print(f"{num} is not prime. It has the following factors: {factors}") else: print(f"{num} is a prime number")
``````

This code defines `check_prime_number()`, which takes the parameter `num` and checks to see if it’s a prime number. If it is, then a message is displayed stating that `num` is a prime number. If it’s not a prime number, then a message is displayed with all the factors of the number.

Note: The above code isn’t the most efficient way to check for prime numbers. If you’re interested in digging deeper, then check out the Sieve of Eratosthenes and Sieve of Atkin for examples of more performant algorithms for finding prime numbers.

Before you look more closely at the function, here are the results using some different numbers:

>>>
``````>>> check_prime_number(44)
44 is not prime. It has the following factors: [(1, 44), (2, 22), (4, 11)] >>> check_prime_number(53)
53 is a prime number >>> check_prime_number(115)
115 is not prime. It has the following factors: [(1, 115), (5, 23)] >>> check_prime_number(997)
997 is a prime number
``````

Digging into the code, you can see it starts by checking if `num` is less than `2`. Prime numbers can only be greater than or equal to `2`. If `num` is less than `2`, then the function doesn’t need to continue. It will `print()` a message and `return`:

``````if num < 2: print(f"{num} must be greater than or equal to 2 to be prime.") return
``````

If `num` is greater than `2`, then the function checks if `num` is a prime number. To check this, the function iterates over all the numbers between `2` and the square root of `num` to see if any divide evenly into `num`. If one of the numbers divides evenly, then a factor has been found, and `num` can’t be a prime number.

Here’s the main part of the function:

``````factors = [(1, num)]
i = 2 while i * i <= num: if num % i == 0: factors.append((i, num//i)) i += 1
``````

There’s a lot to unpack here, so let’s take it step by step.

First, a `factors` list is created with the initial factors, `(1, num)`. This list will be used to store any other factors that are found:

Next, starting with `2`, the code increments `i` until it reaches the square root of `num`. At each iteration, it compares `num` with `i` to see if it’s evenly divisible. The code only needs to check up to and including the square root of `num` because it wouldn’t contain any factors above this:

``````i = 2

while i * i <= num:
if num % i == 0: factors.append((i, num//i))
i += 1
``````

Instead of trying to determine the square root of `num`, the function uses a `while` loop to see if `i * i <= num`. As long as `i * i <= num`, the loop hasn’t reached the square root of `num`.

Inside the `while` loop, the modulo operator checks if `num` is evenly divisible by `i`:

``````factors = [(1, num)]
i = 2 # Start the initial index at 2 while i * i <= num:
if num % i == 0:
factors.append((i, num//i))
i += 1
``````

If `num` is evenly divisible by `i`, then `i` is a factor of `num`, and a tuple of the factors is added to the `factors` list.

Once the `while` loop is complete, the code checks to see if any additional factors were found:

``````if len(factors) > 1: print(f"{num} is not prime. It has the following factors: {factors}")
else: print(f"{num} is a prime number")
``````

If more than one tuple exists in the `factors` list, then `num` can’t be a prime number. For nonprime numbers, the factors are printed out. For prime numbers, the function prints a message stating that `num` is a prime number.

The Python modulo operator can be used to create ciphers. A cipher is a type of algorithm for performing encryption and decryption on an input, usually text. In this section, you’ll look at two ciphers, the Caesar cipher and the Vigenère cipher.

The first cipher that you’ll look at is the Caesar cipher, named after Julius Caesar, who used it to secretly communicate messages. It’s a substitution cipher that uses letter substitution to encrypt a string of text.

The Caesar cipher works by taking a letter to be encrypted and shifting it a certain number of positions to the left or right in the alphabet. Whichever letter is in that position is used as the encrypted character. This same shift value is applied to all characters in the string.

For example, if the shift were `5`, then `A` would shift up five letters to become `F`, `B` would become `G`, and so on. Below you can see the encryption process for the text `REALPYTHON` with a shift of `5`: The resulting cipher is `WJFQUDYMTS`.

Decrypting the cipher is done by reversing the shift. Both the encryption and decryption processes can be described with the following expressions, where `char_index` is the index of the character in the alphabet:

``````encrypted_char_index = (char_index + shift) % 26
decrypted_char_index = (char_index - shift) % 26
``````

This cipher uses the modulo operator to make sure that, when shifting a letter, the index will wrap around if the end of the alphabet is reached. Now that you know how this cipher works, take a look at an implementation:

``````import string def caesar_cipher(text, shift, decrypt=False): if not text.isascii() or not text.isalpha(): raise ValueError("Text must be ASCII and contain no numbers.") lowercase = string.ascii_lowercase uppercase = string.ascii_uppercase result = "" if decrypt: shift = shift * -1 for char in text: if char.islower(): index = lowercase.index(char) result += lowercase[(index + shift) % 26] else: index = uppercase.index(char) result += uppercase[(index + shift) % 26] return result
``````

This code defines a function called `caesar_cipher()`, which has two required parameters and one optional parameter:

• `text` is the text to be encrypted or decrypted.
• `shift` is the number of positions to shift each letter.
• `decrypt` is a Boolean to set if `text` should be decrypted.

`decrypt` is included so that a single function can be used to handle both encryption and decryption. This implementation can handle only alphabetic characters, so the function first checks that `text` is an alphabetic character in the ASCII encoding:

``````def caesar_cipher(text, shift, decrypt=False): if not text.isascii() or not text.isalpha(): raise ValueError("Text must be ASCII and contain no numbers.")
``````

The function then defines three variables to store the `lowercase` ASCII characters, the `uppercase` ASCII characters, and the results of the encryption or decryption:

``````lowercase = string.ascii_lowercase # "abcdefghijklmnopqrstuvwxyz"
uppercase = string.ascii_uppercase # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
result = ""
``````

Next, if the function is being used to decrypt `text`, then it multiplies `shift` by `-1` to make it shift backward:

``````if decrypt: shift = shift * -1
``````

Finally, `caesar_cipher()` loops over the individual characters in `text` and performs the following actions for each `char`:

1. Check if `char` is lowercase or uppercase.
2. Get the `index` of the `char` in either the `lowercase` or `uppercase` ASCII lists.
3. Add a `shift` to this `index` to determine the index of the cipher character to use.
4. Use `% 26` to make sure the shift will wrap back to the start of the alphabet.
5. Append the cipher character to the `result` string.

After the loop finishes iterating over the `text` value, the `result` is returned:

``````for char in text: if char.islower(): index = lowercase.index(char) result += lowercase[(index + shift) % 26] else: index = uppercase.index(char) result += uppercase[(index + shift) % 26] return result
``````

Here’s the full code again:

``````import string def caesar_cipher(text, shift, decrypt=False): if not text.isascii() or not text.isalpha(): raise ValueError("Text must be ASCII and contain no numbers.") lowercase = string.ascii_lowercase uppercase = string.ascii_uppercase result = "" if decrypt: shift = shift * -1 for char in text: if char.islower(): index = lowercase.index(char) result += lowercase[(index + shift) % 26] else: index = uppercase.index(char) result += uppercase[(index + shift) % 26] return result
``````

Now run the code in the Python REPL using the text `meetMeAtOurHideOutAtTwo` with a shift of `10`:

>>>
``````>>> caesar_cipher("meetMeAtOurHideOutAtTwo", 10)
woodWoKdYebRsnoYedKdDgy
``````

The encrypted result is `woodWoKdYebRsnoYedKdDgy`. Using this encrypted text, you can run the decryption to get the original text:

>>>
``````>>> caesar_cipher("woodWoKdYebRsnoYedKdDgy", 10, decrypt=True)
meetMeAtOurHideOutAtTwo
``````

The Caesar cipher is fun to play around with for an introduction to cryptography. While the Caesar cipher is rarely used on its own, it’s the basis for more complex substitution ciphers. In the next section, you’ll look at one of the Caesar cipher’s descendants, the Vigenère cipher.

The Vigenère cipher is a polyalphabetic substitution cipher. To perform its encryption, it employs a different Caesar cipher for each letter of the input text. The Vigenère cipher uses a keyword to determine which Caesar cipher should be used to find the cipher letter.

You can see an example of the encryption process in the following image. In this example, the input text `REALPYTHON` is encrypted using the keyword `MODULO`: For each letter of the input text, `REALPYTHON`, a letter from the keyword `MODULO` is used to determine which Caesar cipher column should be selected. If the keyword is shorter than the input text, as is the case with `MODULO`, then the letters of the keyword are repeated until all letters of the input text have been encrypted.

Below is an implementation of the Vigenère cipher. As you’ll see, the modulo operator is used twice in the function:

``````import string def vigenere_cipher(text, key, decrypt=False): if not text.isascii() or not text.isalpha() or not text.isupper(): raise ValueError("Text must be uppercase ASCII without numbers.") uppercase = string.ascii_uppercase # "ABCDEFGHIJKLMNOPQRSTUVWXYZ" results = "" for i, char in enumerate(text): current_key = key[i % len(key)] char_index = uppercase.index(char) key_index = uppercase.index(current_key) if decrypt: index = char_index - key_index + 26 else: index = char_index + key_index results += uppercase[index % 26] return results
``````

You may have noticed that the signature for `vigenere_cipher()` is quite similar to `caesar_cipher()` from the previous section:

``````def vigenere_cipher(text, key, decrypt=False): if not text.isascii() or not text.isalpha() or not text.isupper(): raise ValueError("Text must be uppercase ASCII without numbers.") uppercase = string.ascii_uppercase results = ""
``````

The main difference is that, instead of a `shift` parameter, `vigenere_cipher()` takes a `key` parameter, which is the keyword to be used during encryption and decryption. Another difference is the addition of `text.isupper()`. Based on this implementation, `vigenere_cipher()` can only accept input text that is all uppercase.

Like `caesar_cipher()`, `vigenere_cipher()` iterates over each letter of the input text to encrypt or decrypt it:

``````for i, char in enumerate(text): current_key = key[i % len(key)]
``````

In the above code, you can see the function’s first use of the modulo operator:

``````current_key = key[i % len(key)]
``````

Here, the `current_key` value is determined based on an index returned from `i % len(key)`. This index is used to select a letter from the `key` string, such as `M` from `MODULO`.

The modulo operator allows you to use any length keyword regardless of the length of the `text` to be encrypted. Once the index `i`, the index of the character currently being encrypted, equals the length of the keyword, it will start over from the beginning of the keyword.

For each letter of the input text, several steps determine how to encrypt or decrypt it:

1. Determine the `char_index` based on the index of `char` inside `uppercase`.
2. Determine the `key_index` based on the index of `current_key` inside `uppercase`.
3. Use `char_index` and `key_index` to get the index for the encrypted or decrypted character.

Take a look at these steps in the code below:

``````char_index = uppercase.index(char)
key_index = uppercase.index(current_key) if decrypt: index = char_index - key_index + 26
else: index = char_index + key_index
``````

You can see that the indices for decryption and encryption are calculated differently. That’s why `decrypt` is used in this function. This way, you can use the function for both encryption and decryption.

After the `index` is determined, you find the function’s second use of the modulo operator:

``````results += uppercase[index % 26]
``````

`index % 26` ensures that the `index` of the character doesn’t exceed `25`, thus making sure it stays inside the alphabet. With this index, the encrypted or decrypted character is selected from `uppercase` and appended to `results`.

Here’s the full code the Vigenère cipher again:

``````import string def vigenere_cipher(text, key, decrypt=False): if not text.isascii() or not text.isalpha() or not text.isupper(): raise ValueError("Text must be uppercase ASCII without numbers.") uppercase = string.ascii_uppercase # "ABCDEFGHIJKLMNOPQRSTUVWXYZ" results = "" for i, char in enumerate(text): current_key = key[i % len(key)] char_index = uppercase.index(char) key_index = uppercase.index(current_key) if decrypt: index = char_index - key_index + 26 else: index = char_index + key_index results += uppercase[index % 26] return results
``````

Now go ahead and run it in the Python REPL:

>>>
``````>>> vigenere_cipher(text="REALPYTHON", key="MODULO")
DSDFAMFVRH >>> encrypted = vigenere_cipher(text="REALPYTHON", key="MODULO")
>>> print(encrypted)
DSDFAMFVRH >>> vigenere_cipher(encrypted, "MODULO", decrypt=True)
REALPYTHON
``````

Nice! You now have a working Vigenère cipher for encrypting text strings.