Movatterモバイル変換


[0]ホーム

URL:


— FREE Email Series —

🐍 Python Tricks 💌

Python Tricks Dictionary Merge

🔒 No spam. Unsubscribe any time.

Browse TopicsGuided Learning Paths
Basics Intermediate Advanced
aialgorithmsapibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnewsnumpyprojectspythonstdlibtestingtoolsweb-devweb-scraping

Table of Contents

Recommended Course

Python Modulo in Practice: How to Use the % Operator

Python Modulo: Using the % Operator

33m · 6 lessons

Python Modulo in Practice: How to Use the % Operator

Python Modulo in Practice: How to Use the % Operator

byJason Van SchooneveldReading time estimate 40mbasicspython

Table of Contents

Remove ads

Recommended Course

Python Modulo: Using the % Operator(33m)

Python supports a wide range ofarithmetic operators that you can use when working withnumbers in your code. One of these operators is themodulo operator (%), which returns the remainder of dividing two numbers.

In this tutorial, you’ll learn:

  • Howmodulo works in mathematics
  • How to use the Python modulo operator with differentnumeric types
  • How Python calculates the results of amodulo operation
  • How to override.__mod__() in your classes to use them with the modulo operator
  • How to use the Python modulo operator to solvereal-world problems

The Python modulo operator can sometimes be overlooked. But having a good understanding of this operator will give you an invaluable tool in your Python tool belt.

Free Bonus:Click here to get a Python Cheat Sheet and learn the basics of Python 3, like working with data types, dictionaries, lists, and Python functions.

Modulo in Mathematics

The termmodulo comes from a branch of mathematics calledmodular arithmetic. Modular arithmetic deals with integer arithmetic on a circular number line that has a fixed set of numbers. All arithmetic operations performed on this number line will wrap around when they reach a certain number called themodulus.

A classic example of modulo in modular arithmetic is the twelve-hour clock. A twelve-hour clock has a fixed set of values, from 1 to 12. When counting on a twelve-hour clock, you count up to the modulus 12 and then wrap back to 1. A twelve-hour clock can be classified as “modulo 12,” sometimes shortened to “mod 12.”

The modulo operator is used when you want to compare a number with the modulus and get the equivalent number constrained to the range of the modulus.

For example, say you want to determine what time it would be nine hours after 8:00 a.m. On a twelve-hour clock, you can’t simply add 9 to 8 because you would get 17. You need to take the result, 17, and usemod to get its equivalent value in a twelve-hour context:

Text
8 o'clock + 9 = 17 o'clock17 mod 12 = 5

17 mod 12 returns5. This means that nine hours past 8:00 a.m. is 5:00 p.m. You determined this by taking the number17 and applying it to amod 12 context.

Now, if you think about it,17 and5 are equivalent in amod 12 context. If you were to look at the hour hand at 5:00 and 17:00, it would be in the same position. Modular arithmetic has an equation to describe this relationship:

Text
a ≡ b (mod n)

This equation reads “a andb are congruent modulon.” This means thata andb are equivalent inmod n as they have the same remainder when divided byn. In the above equation,n is themodulus for botha andb. Using the values17 and5 from before, the equation would look like this:

Text
17 ≡ 5 (mod 12)

This reads “17 and5 are congruent modulo12.”17 and5 have the same remainder,5, when divided by12. So inmod 12, the numbers17 and5 are equivalent.

You can confirm this using division:

Text
17 / 12 = 1 R 55 / 12 = 0 R 5

Both of the operations have the same remainder,5, so they’re equivalent modulo12.

Now, this may seem like a lot of math for a Python operator, but having this knowledge will prepare you to use the modulo operator in the examples later in this tutorial. In the next section, you’ll look at the basics of using the Python modulo operator with the numeric typesint andfloat.

Python Modulo Operator Basics

The modulo operator, like the other arithmetic operators, can be used with the numeric typesint andfloat. As you’ll see later on, it can also be used with other types likemath.fmod(),decimal.Decimal, and your own classes.

Modulo Operator Withint

Most of the time you’ll use the modulo operator with integers. The modulo operator, when used with two positive integers, will return the remainder of standardEuclidean division:

Python
>>>15%43>>>17%125>>>240%136>>>10%1610

Be careful! Just like with the division operator (/), Python will return aZeroDivisionError if you try to use the modulo operator with a divisor of0:

Python
>>>22%0ZeroDivisionError: integer division or modulo by zero

Next, you’ll take a look at using the modulo operator with afloat.

Modulo Operator Withfloat

Similar toint, the modulo operator used with afloat will return the remainder of division, but as afloat value:

Python
>>>12.5%5.51.5>>>17.0%12.05.0

An alternative to using afloat with the modulo operator is to usemath.fmod() to perform modulo operations onfloat values:

Python
>>>importmath>>>math.fmod(12.5,5.5)1.5>>>math.fmod(8.5,2.5)1.0

The official Python docssuggest usingmath.fmod() over the Python modulo operator when working withfloat values because of the waymath.fmod() calculates the result of the modulo operation. If you’re using a negative operand, then you may see different results betweenmath.fmod(x, y) andx % y. You’ll explore using the modulo operator with negative operands in more detail in the next section.

Just like other arithmetic operators, the modulo operator andmath.fmod() may encounter rounding and precision issues when dealing withfloating-point arithmetic:

Python
>>>13.3%1.10.09999999999999964>>>importmath>>>math.fmod(13.3,1.1)0.09999999999999964

If maintaining floating-point precision is important to your application, then you can use the modulo operator withdecimal.Decimal. You’ll look at thislater in this tutorial.

Modulo Operator With a Negative Operand

All modulo operations you’ve seen up to this point have used two positive operands and returned predictable results. When a negative operand is introduced, things get more complicated.

As it turns out, the way that computers determine the result of a modulo operation with a negative operand leaves ambiguity as to whether the remainder should take the sign of thedividend (the number being divided) or the sign of thedivisor (the number by which the dividend is divided). Different programming languages handle this differently.

For example, inJavaScript, the remainder will take the sign of the dividend:

JavaScript
8%-3=2

The remainder in this example,2, is positive since it takes the sign of the dividend,8. In Python and other languages, the remainder will take the sign of the divisor instead:

Python
8%-3=-1

Here you can see that the remainder,-1, takes the sign of the divisor,-3.

You may be wondering why the remainder in JavaScript is2 and the remainder in Python is-1. This has to do with how different languages determine the outcome of a modulo operation. Languages in which the remainder takes the sign of the dividend use the following equation to determine the remainder:

Text
r = a - (n * trunc(a/n))

There are three variables this equation:

  1. r is the remainder.
  2. a is the dividend.
  3. n is the divisor.

trunc() in this equation means that it usestruncated division, which will always round a negative number toward zero. For more clarification, see the steps of the modulo operation below using8 as the dividend and-3 as the divisor:

Text
r = 8 - (-3 * trunc(8/-3))r = 8 - (-3 * trunc(-2.666666666667))r = 8 - (-3 * -2) # Rounded toward 0r = 8 - 6r = 2

Here you can see how a language like JavaScript gets the remainder2. Python and other languages in which the remainder takes the sign of the divisor use the following equation:

Text
r = a - (n * floor(a/n))

floor() in this equation means that it usesfloor division. With positive numbers, floor division will return the same result as truncated division. But with a negative number, floor division will round the result down, away from zero:

Text
r = 8 - (-3 * floor(8/-3))r = 8 - (-3 * floor(-2.666666666667))r = 8 - (-3 * -3) # Rounded away from 0r = 8 - 9r = -1

Here you can see that the result is-1.

Now that you understand where the difference in the remainder comes from, you may be wondering why this matters if you only use Python. Well, as it turns out, not all modulo operations in Python are the same. While the modulo used with theint andfloat types will take the sign of the divisor, other types will not.

You can see an example of this when you compare the results of8.0 % -3.0 andmath.fmod(8.0, -3.0):

Python
>>>8.0%-3-1.0>>>importmath>>>math.fmod(8.0,-3.0)2.0

math.fmod() takes the sign of the dividend using truncated division, whereasfloat uses the sign of the divisor. Later in this tutorial, you’ll see another Python type that uses the sign of the dividend,decimal.Decimal.

Modulo Operator anddivmod()

Python has the built-in functiondivmod(), which internally uses the modulo operator.divmod() takes two parameters and returns a tuple containing the results of floor division and modulo using the supplied parameters.

Below is an example of usingdivmod() with37 and5:

Python
>>>divmod(37,5)(7, 2)>>>37//57>>>37%52

You can see thatdivmod(37, 5) returns the tuple(7, 2). The7 is the result of the floor division of37 and5. The2 is the result of37 modulo5.

Below is an example in which the second parameter is a negative number. As discussed in the previous section, when the modulo operator is used with anint, the remainder will take the sign of the divisor:

Python
>>>divmod(37,-5)(-8, -3)>>>37//-5-8>>>37%-5-3 # Result has the sign of the divisor

Now that you’ve had a chance to see the modulo operator used in several scenarios, it’s important to take a look at how Python determines the precedence of the modulo operator when used with other arithmetic operators.

Modulo Operator Precedence

Like other Python operators, there are specific rules for the modulo operator that determine itsprecedence when evaluating expressions. The modulo operator (%) shares the same level of precedence as the multiplication (*), division (/), and floor division (//) operators.

Take a look at an example of the modulo operator’s precedence below:

Python
>>>4*10%12-9-5

Both the multiplication and modulo operators have the same level of precedence, so Python will evaluate them from left to right. Here are the steps for the above operation:

  1. 4 * 10 is evaluated, resulting in40 % 12 - 9.
  2. 40 % 12 is evaluated, resulting in4 - 9.
  3. 4 - 9 is evaluated, resulting in-5.

If you want to override the precedence of other operators, then you can use parentheses to surround the operation you want to be evaluated first:

Python
>>>4*10%(12-9)1

In this example,(12 - 9) is evaluated first, followed by4 * 10 and finally40 % 3, which equals1.

Python Modulo Operator in Practice

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.

How to Check if a Number Is Even or Odd

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 of2, you can check any number to see if it’s evenly divisible by2. If it is evenly divisible, then it’s an even number.

Take a look atis_even() which checks to see if thenum parameter is even:

Python
defis_even(num):returnnum%2==0

Herenum % 2 will equal0 ifnum is even and1 ifnum is odd. Checking against0 will return aBoolean ofTrue orFalse based on whether or notnum is even.

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

Python
defis_odd(num):returnnum%2!=0

This function will returnTrue ifnum % 2 does not equal0, meaning that there’s a remainder provingnum is an odd number. Now, you may be wondering if you could use the following function to determine ifnum is an odd number:

Python
defis_odd(num):returnnum%2==1

The answer to this question is yesand 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 with1 as not all modulo operations in Python will return the same remainder.

You can see why in the following examples:

Python
>>>-3%21>>>3%-2-1

In the second example, the remainder takes the sign of the negative divisor and returns-1. In this case, the Boolean check3 % -2 == 1 would returnFalse.

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

Python
>>>-2%20>>>2%-20

If you stick to comparing a Python modulo operation with0, 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.

How to Run Code at Specific Intervals in a Loop

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:

Python
defsplit_names_into_rows(name_list,modulus=3):forindex,nameinenumerate(name_list,start=1):print(f"{name:-^15} ",end="")ifindex%modulus==0:print()print()

This code definessplit_names_into_rows(), which takes two parameters.name_list is alist 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 overname_list and start a new row after it hits themodulus value.

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

Python
>>>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 to3, but you can specify any number:

Python
>>>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 usesenumerate() to iterate overname_list, assigning the current item in the list toname and a count value toindex. You can see that the optionalstart argument forenumerate() is set to1. This means that theindex count will start at1 instead of0:

Python
forindex,nameinenumerate(name_list,start=1):

Next, inside the loop, the function callsprint() to outputname to the current row. Theend parameter forprint() is an emptystring ("") so it won’t output a newline at the end of the string. Anf-string is passed toprint(), which uses thestring output formatting syntax that Python provides:

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

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

  • Output at least15 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 ofsplit_names_into_rows():

Python
ifindex%modulus==0:print()

This code takes the current iterationindex and, using the modulo operator, compares it withmodulus. If the result equals0, then it can run interval-specific code. In this case, the function callsprint() to add a newline, which starts a new row.

The above code is only one example. Using the patternindex % 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.

How to Create 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 theturtle library to draw a shape:

Python
importturtleimportrandomdefdraw_with_cyclic_iteration():colors=["green","cyan","orange","purple","red","yellow","white"]turtle.bgcolor("gray8")# Hex: #333333turtle.pendown()turtle.pencolor(random.choice(colors))# First color is randomi=0# Initial indexwhileTrue:i=(i+1)%6# Update the indexturtle.pensize(i)# Set pensize to iturtle.forward(225)turtle.right(170)# Pick a random colorifi==0:turtle.pencolor(random.choice(colors))

The above code uses aninfinite 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 untili is reset back to0. If you run the code, then you should get something similar to this:

Example of cyclic iteration using Python mod (%) Operator

The important parts of this code are highlighted below:

Python
importturtleimportrandomdefdraw_with_cyclic_iteration():colors=["green","cyan","orange","purple","red","yellow","white"]turtle.bgcolor("gray8")# Hex: #333333turtle.pendown()turtle.pencolor(random.choice(colors))i=0# Initial indexwhileTrue:i=(i+1)%6# Update the indexturtle.pensize(i)# Set pensize to iturtle.forward(225)turtle.right(170)# Pick a random colorifi==0:turtle.pencolor(random.choice(colors))

Each time through the loop,i is updated based on the results of(i + 1) % 6. This newi value is used to increase the.pensize with each iteration. Oncei reaches5,(i + 1) % 6 will equal0, andi will reset back to0.

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

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

Wheni is reset back to0, the.pencolor changes to a new random color as seen below:

Python
ifi==0:turtle.pencolor(random.choice(colors))

The code in this section uses6 as the modulus, but you could set it to any number to adjust how many times the loop will iterate before resetting the valuei.

How to Convert Units

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:

Python
defconvert_inches_to_feet(total_inches):inches=total_inches%12feet=total_inches//12print(f"{total_inches} inches ={feet} feet and{inches} inches")

Here’s an example of the function in use:

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

As you can see from the output,450 % 12 returns6, which is the remaining inches that weren’t evenly divided into feet. The result of450 // 12 is37, 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:

Python
defconvert_minutes_to_days(total_mins):days=total_mins//1440extra_minutes=total_mins%1440hours=extra_minutes//60minutes=extra_minutes%60print(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 withtotal_mins // 1440, where1440 is the number of minutes in a day
  2. Calculates anyextra_minutes left over withtotal_mins % 1440
  3. Uses theextra_minutes to get the evenly divisiblehours and any extraminutes

You can see how it works below:

Python
>>>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 usedivmod() 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 withdivmod():

Python
defconvert_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 intofeet andinches.

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

Python
>>>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 updateconvert_minutes_to_days() as well:

Python
defconvert_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")

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

Python
>>>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

Usingdivmod() 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.

How to Determine if a Number Is a Prime Number

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

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

Python
defcheck_prime_number(num):ifnum<2:print(f"{num} must be greater than or equal to 2 to be prime.")returnfactors=[(1,num)]i=2whilei*i<=num:ifnum%i==0:factors.append((i,num//i))i+=1iflen(factors)>1:print(f"{num} is not prime. It has the following factors:{factors}")else:print(f"{num} is a prime number")

This code definescheck_prime_number(), which takes the parameternum and checks to see if it’s a prime number. If it is, then a message is displayed stating thatnum 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 theSieve of Eratosthenes andSieve 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:

Python
>>>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 ifnum is less than2. Prime numbers can only be greater than or equal to2. Ifnum is less than2, then the function doesn’t need to continue. It willprint() a message andreturn:

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

Ifnum is greater than2, then the function checks ifnum is a prime number. To check this, the function iterates over all the numbers between2 and thesquare root ofnum to see if any divide evenly intonum. If one of the numbers divides evenly, then a factor has been found, andnum can’t be a prime number.

Here’s the main part of the function:

Python
factors=[(1,num)]i=2whilei*i<=num:ifnum%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, afactors list is created with the initial factors,(1, num). This list will be used to store any other factors that are found:

Python
factors=[(1,num)]

Next, starting with2, the code incrementsi until it reaches the square root ofnum. At each iteration, it comparesnum withi to see if it’s evenly divisible. The code only needs to check up to and including the square root ofnum because it wouldn’t contain any factors above this:

Python
i=2whilei*i<=num:ifnum%i==0:factors.append((i,num//i))i+=1

Instead of trying to determine the square root ofnum, the function uses awhile loop to see ifi * i <= num. As long asi * i <= num, the loop hasn’t reached the square root ofnum.

Inside thewhile loop, the modulo operator checks ifnum is evenly divisible byi:

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

Ifnum is evenly divisible byi, theni is a factor ofnum, and a tuple of the factors is added to thefactors list.

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

Python
iflen(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 thefactors list, thennum can’t be a prime number. For nonprime numbers, the factors are printed out. For prime numbers, the function prints a message stating thatnum is a prime number.

How to Implement Ciphers

The Python modulo operator can be used to createciphers. A cipher is a type of algorithm for performing encryption and decryption on aninput, usually text. In this section, you’ll look at two ciphers, theCaesar cipher and theVigenère cipher.

Caesar Cipher

The first cipher that you’ll look at is theCaesar cipher, named after Julius Caesar, who used it to secretly communicate messages. It’s asubstitution 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 were5, thenA would shift up five letters to becomeF,B would becomeG, and so on. Below you can see the encryption process for the textREALPYTHON with a shift of5:

Caesar Cipher using Python mod (%) Operator

The resulting cipher isWJFQUDYMTS.

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

Python
encrypted_char_index=(char_index+shift)%26decrypted_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:

Python
importstringdefcaesar_cipher(text,shift,decrypt=False):ifnottext.isascii()ornottext.isalpha():raiseValueError("Text must be ASCII and contain no numbers.")lowercase=string.ascii_lowercaseuppercase=string.ascii_uppercaseresult=""ifdecrypt:shift=shift*-1forcharintext:ifchar.islower():index=lowercase.index(char)result+=lowercase[(index+shift)%26]else:index=uppercase.index(char)result+=uppercase[(index+shift)%26]returnresult

This code defines a function calledcaesar_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 iftext 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 thattext is an alphabetic character in the ASCII encoding:

Python
defcaesar_cipher(text,shift,decrypt=False):ifnottext.isascii()ornottext.isalpha():raiseValueError("Text must be ASCII and contain no numbers.")

The function then defines three variables to store thelowercase ASCII characters, theuppercase ASCII characters, and the results of the encryption or decryption:

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

Next, if the function is being used to decrypttext, then it multipliesshift by-1 to make it shift backward:

Python
ifdecrypt:shift=shift*-1

Finally,caesar_cipher() loops over the individual characters intext and performs the following actions for eachchar:

  1. Check ifchar is lowercase or uppercase.
  2. Get theindex of thechar in either thelowercase oruppercase ASCII lists.
  3. Add ashift to thisindex 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 theresult string.

After the loop finishes iterating over thetext value, theresult is returned:

Python
forcharintext:ifchar.islower():index=lowercase.index(char)result+=lowercase[(index+shift)%26]else:index=uppercase.index(char)result+=uppercase[(index+shift)%26]returnresult

Here’s the full code again:

Python
importstringdefcaesar_cipher(text,shift,decrypt=False):ifnottext.isascii()ornottext.isalpha():raiseValueError("Text must be ASCII and contain no numbers.")lowercase=string.ascii_lowercaseuppercase=string.ascii_uppercaseresult=""ifdecrypt:shift=shift*-1forcharintext:ifchar.islower():index=lowercase.index(char)result+=lowercase[(index+shift)%26]else:index=uppercase.index(char)result+=uppercase[(index+shift)%26]returnresult

Now run the code in thePython REPL using the textmeetMeAtOurHideOutAtTwo with a shift of10:

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

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

Python
>>>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.

Vigenère Cipher

TheVigenère cipher is apolyalphabetic 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 textREALPYTHON is encrypted using the keywordMODULO:

Vigenère Cipher using Python mod (%) Operator

For each letter of the input text,REALPYTHON, a letter from the keywordMODULO is used to determine which Caesar cipher column should be selected. If the keyword is shorter than the input text, as is the case withMODULO, 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:

Python
importstringdefvigenere_cipher(text,key,decrypt=False):ifnottext.isascii()ornottext.isalpha()ornottext.isupper():raiseValueError("Text must be uppercase ASCII without numbers.")uppercase=string.ascii_uppercase# "ABCDEFGHIJKLMNOPQRSTUVWXYZ"results=""fori,charinenumerate(text):current_key=key[i%len(key)]char_index=uppercase.index(char)key_index=uppercase.index(current_key)ifdecrypt:index=char_index-key_index+26else:index=char_index+key_indexresults+=uppercase[index%26]returnresults

You may have noticed that the signature forvigenere_cipher() is quite similar tocaesar_cipher() from the previous section:

Python
defvigenere_cipher(text,key,decrypt=False):ifnottext.isascii()ornottext.isalpha()ornottext.isupper():raiseValueError("Text must be uppercase ASCII without numbers.")uppercase=string.ascii_uppercaseresults=""

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

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

Python
fori,charinenumerate(text):current_key=key[i%len(key)]

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

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

Here, thecurrent_key value is determined based on an index returned fromi % len(key). This index is used to select a letter from thekey string, such asM fromMODULO.

The modulo operator allows you to use any length keyword regardless of the length of thetext to be encrypted. Once the indexi, 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 thechar_index based on the index ofchar insideuppercase.
  2. Determine thekey_index based on the index ofcurrent_key insideuppercase.
  3. Usechar_index andkey_index to get the index for the encrypted or decrypted character.

Take a look at these steps in the code below:

Python
char_index=uppercase.index(char)key_index=uppercase.index(current_key)ifdecrypt:index=char_index-key_index+26else:index=char_index+key_index

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

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

Python
results+=uppercase[index%26]

index % 26 ensures that theindex of the character doesn’t exceed25, thus making sure it stays inside the alphabet. With this index, the encrypted or decrypted character is selected fromuppercase and appended toresults.

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

Python
importstringdefvigenere_cipher(text,key,decrypt=False):ifnottext.isascii()ornottext.isalpha()ornottext.isupper():raiseValueError("Text must be uppercase ASCII without numbers.")uppercase=string.ascii_uppercase# "ABCDEFGHIJKLMNOPQRSTUVWXYZ"results=""fori,charinenumerate(text):current_key=key[i%len(key)]char_index=uppercase.index(char)key_index=uppercase.index(current_key)ifdecrypt:index=char_index-key_index+26else:index=char_index+key_indexresults+=uppercase[index%26]returnresults

Now go ahead and run it in the Python REPL:

Python
>>>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.

Python Modulo Operator Advanced Uses

In this final section, you’ll take your modulo operator knowledge to the next level by using it withdecimal.Decimal. You’ll also look at how you can add.__mod__() to your custom classes so they can be used with the modulo operator.

Using the Python Modulo Operator Withdecimal.Decimal

Earlier in this tutorial, you saw how you can use the modulo operator with numeric types likeint andfloat as well as withmath.fmod(). You can also use the modulo operator withDecimal from thedecimal module. You usedecimal.Decimal when you want discrete control of the precision of floating-point arithmetic operations.

Here are some examples of using whole integers withdecimal.Decimal and the modulo operator:

Python
>>>importdecimal>>>decimal.Decimal(15)%decimal.Decimal(4)Decimal('3')>>>decimal.Decimal(240)%decimal.Decimal(13)Decimal('6')

Here are some floating-point numbers used withdecimal.Decimal and the modulo operator:

Python
>>>decimal.Decimal("12.5")%decimal.Decimal("5.5")Decimal('1.5')>>>decimal.Decimal("13.3")%decimal.Decimal("1.1")Decimal('0.1')

All modulo operations withdecimal.Decimal return the same results as other numeric types, except when one of the operands is negative. Unlikeint andfloat, but likemath.fmod(),decimal.Decimal uses the sign of the dividend for the results.

Take a look at the examples below comparing the results of using the modulo operator with standardint andfloat values and withdecimal.Decimal:

Python
>>>-17%31 # Sign of the divisor>>>decimal.Decimal(-17)%decimal.Decimal(3)Decimal(-2) # Sign of the dividend>>>17%-3-1 # Sign of the divisor>>>decimal.Decimal(17)%decimal.Decimal(-3)Decimal("2") # Sign of dividend>>>-13.3%1.11.0000000000000004 # Sign of the divisor>>>decimal.Decimal("-13.3")%decimal.Decimal("1.1")Decimal("-0.1") # Sign of the dividend

Compared withmath.fmod(),decimal.Decimal will have the same sign, but the precision will be different:

Python
>>>decimal.Decimal("-13.3")%decimal.Decimal("1.1")Decimal("-0.1")>>>math.fmod(-13.3,1.1)-0.09999999999999964

As you can see from the above examples, working withdecimal.Decimal and the modulo operator is similar to working with other numeric types. You just need to keep in mind how it determines the sign of the result when working with a negative operand.

In the next section, you’ll look at how you can override the modulo operator in your classes to customize its behavior.

Using the Python Modulo Operator With Custom Classes

The Pythondata model allows to you override the built-in methods in a Python object to customize its behavior. In this section, you’ll look at how to override.__mod__() so that you can use the modulo operator with your own classes.

For this example, you’ll be working with aStudent class. This class will track the amount of time a student has studied. Here’s the initialStudent class:

Python
classStudent:def__init__(self,name):self.name=nameself.study_sessions=[]defadd_study_sessions(self,sessions):self.study_sessions+=sessions

TheStudent class is initialized with aname parameter and starts with an empty list,study_sessions, which will hold a list of integers representing minutes studied per session. There’s also.add_study_sessions(), which takes asessions parameter that should be a list of study sessions to add tostudy_sessions.

Now, if you remember from theconverting units section above,convert_minutes_to_day() used the Python modulo operator to converttotal_mins into days, hours, and minutes. You’ll now implement a modified version of that method to see how you can use your custom class with the modulo operator:

Python
deftotal_study_time_in_hours(student,total_mins):hours=total_mins//60minutes=total_mins%60print(f"{student.name} studied{hours} hours and{minutes} minutes")

You can use this function with theStudent class to display the total hours aStudent has studied. Combined with theStudent class above, the code will look like this:

Python
classStudent:def__init__(self,name):self.name=nameself.study_sessions=[]defadd_study_sessions(self,sessions):self.study_sessions+=sessionsdeftotal_study_time_in_hours(student,total_mins):hours=total_mins//60minutes=total_mins%60print(f"{student.name} studied{hours} hours and{minutes} minutes")

If you load this module in the Python REPL, then you can use it like this:

Python
>>>jane=Student("Jane")>>>jane.add_study_sessions([120,30,56,260,130,25,75])>>>total_mins=sum(jane.study_sessions)>>>total_study_time_in_hours(jane,total_mins)Jane studied 11 hours and 36 minutes

The above code prints out the total hoursjane studied. This version of the code works, but it requires the extra step of summingstudy_sessions to gettotal_mins before callingtotal_study_time_in_hours().

Here’s how you can modify theStudent class to simplify the code:

Python
classStudent:def__init__(self,name):self.name=nameself.study_sessions=[]defadd_study_sessions(self,sessions):self.study_sessions+=sessionsdef__mod__(self,other):returnsum(self.study_sessions)%otherdef__floordiv__(self,other):returnsum(self.study_sessions)//other

By overriding.__mod__() and.__floordiv__(), you can use aStudent instance with the modulo operator. Calculating thesum() ofstudy_sessions is included in theStudent class as well.

With these modifications, you can use aStudent instance directly intotal_study_time_in_hours(). Astotal_mins is no longer needed, you can remove it:

Python
deftotal_study_time_in_hours(student):hours=student//60minutes=student%60print(f"{student.name} studied{hours} hours and{minutes} minutes")

Here’s the full code after modifications:

Python
classStudent:def__init__(self,name):self.name=nameself.study_sessions=[]defadd_study_sessions(self,sessions):self.study_sessions+=sessionsdef__mod__(self,other):returnsum(self.study_sessions)%otherdef__floordiv__(self,other):returnsum(self.study_sessions)//otherdeftotal_study_time_in_hours(student):hours=student//60minutes=student%60print(f"{student.name} studied{hours} hours and{minutes} minutes")

Now, calling the code in the Python REPL, you can see it’s much more succinct:

Python
>>>jane=Student("Jane")>>>jane.add_study_sessions([120,30,56,260,130,25,75])>>>total_study_time_in_hours(jane)Jane studied 11 hours and 36 minutes

By overriding.__mod__(), you allow your custom classes to behave more like Python’s built-in numeric types.

Conclusion

At first glance, the Python modulo operator may not grab your attention. Yet, as you’ve seen, there’s so much to this humble operator. From checking for even numbers to encrypting text with ciphers, you’ve seen many different uses for the modulo operator.

In this tutorial, you’ve learned how to:

  • Use themodulo operator withint,float,math.fmod(),divmod(), anddecimal.Decimal
  • Calculate the results of amodulo operation
  • Solvereal-world problems using the modulo operator
  • Override.__mod__() in your own classes to use them with the modulo operator

With the knowledge you’ve gained in this tutorial, you can now start using the modulo operator in your own code with great success. Happy Pythoning!

Recommended Course

Python Modulo: Using the % Operator(33m)

🐍 Python Tricks 💌

Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

AboutJason Van Schooneveld

Jason is a software developer based in Taipei. When he's not tinkering with electronics or building Django web apps, you can find him hiking the mountains of Taiwan or brushing up on his Chinese.

» More about Jason

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

MasterReal-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

MasterReal-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.


Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics:basicspython

Related Courses:

Related Tutorials:

Keep reading Real Python by creating a free account or signing in:

Already have an account?Sign-In

Almost there! Complete this form and click the button below to gain instant access:

Python Logo

Get the Python Cheat Sheet (Free PDF)

🔒 No spam. We take your privacy seriously.


[8]ページ先頭

©2009-2026 Movatter.jp