Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Python fundamentals: a comprehensive Python loops tutorial

Knowing how to create loops and iterative code is a vital skill. Here's how to code basic loops in Python, such as for loops, while loops, nested loops, and more.

Jul 22, 2024 • 12 Minute Read

Please set an alt value for this image...
  • Software Development
  • Guides

Let’s talk about the basics of loops and iteration in Python. After you work your way through this article, you’ll be ready to deal with loops in the wild.


Table of contents


What is iteration in Python?

First things first, what’s iteration? Iteration is the concept of executing a block of code repeatedly or cycling through a set of values. It’s indicating a repetitive action. You can achieve iteration with loops, though loops aren’t the only way. For example, you can also achieve iteration with recursion—a function that calls itself repeatedly. But we’ll focus on loops in this article.

What are loops in Python?

So, what are loops? Loops are a programming construct that implement iteration. That sentence may sound complicated, but it’s actually quite simple: A loop is an instruction to repeat a block of code. And since you don’t want to repeat the code block forever, you can use two common ways to control the number of cycles:

  • Execute the code block as long as a certain condition is true (while loop)
  • Execute the code block for every element in a sequence (for loop)

Real-life examples of a loop in Python

Here’s a non-code real-life loop example to paint a picture for you. Suppose you're a teacher and you need to grade exams. You’ll go ahead and grade each exam, but that’s not the only iteration going on. For every exam, you’re grading every question. Grading your students’ work requires the repetition of grading each exam, and grading each exam requires the repetition of grading each question.

Let’s look at another example. Imagine you’re a dishwasher in a busy restaurant. During your shift, you do dishwashing activities in a loop. You take the dirty dishes, scrub and rinse them, put them in the dishwasher, run the dishwasher, unload them, return them to the kitchen, and get them back dirty only to clean them again. And you repeat this process until your shift is over or there are no more dirty dishes.

What do these examples have in common with loops and iteration? You’ll repeat a specific set of actions (grading exams, washing dishes) until a certain condition becomes true—until all students have their grades, your shift is over, or all dishes are clean.

You can handle this iteration with two types of loops: for and while. They differ a little in use cases, so let’s start with the for loop.

The for loop in Python

With the for loop, you can iterate through any sequence, processing one item at a time. A sequence is a variable that holds multiple elements. It could be a list, tuple, or string.

Here’s a basic example where we iterate through a list of strings:

      # Example list
languages = ["Java", "Python", "C#"]

# Iterate through the list
for language in languages:
    print(f"Language : {language }")

    

It’s going to print the language for every element in the list of languages. During an iteration, the element currently iterated over is stored in language. The loop is done when it’s gone over every element. The list remains the same.

The range() function and the for loop in Python

We can make the for loop more interesting by combining it with the built-in range function. The range() function is used to generate a sequence of numbers, which is handy for repeating an action a specific number of times. Let’s say I want to print the numbers 0 – 4. Here’s how I could do that using the range function:

      # Print numbers from 0 to 4
for i in range(5):
    print(i)
    

Having this range() function allows us to iterate over the indices of a list in Python. We use this function with the len() function (this determines the length of a sequence). This approach is particularly useful when you need to access the index of each element during the iteration, perhaps to modify the elements based on their positions or to reference other parallel lists.

Let’s say we have a list of exam scores. The exam results were poor, and we want to increase each score by 10%, but only if the score is below 90. Otherwise we increase the score to 100.

      scores = [34, 65, 54, 92, 42, 78, 90, 46, 59, 51]

# Iterate over indices to modify scores
for i in range(len(scores)):
    if scores[i] < 90:
        scores[i] *= 1.10  # Increase score by 10%
    else:
        scores[i] = 100

print("Updated scores:", scores)

    

This generates a range object from 0 to the length of the scores list minus one, effectively providing the indices of the list. Inside the loop, we check if the score at each index i is less than 90. If true, we modify the score by multiplying it by 1.10. If it’s above 90, we set the score to 100. We directly modify the elements in the list based on their indices.

Without the index, you couldn’t do this. Thank you, range() function!

We could apply this to our dishwashing example as well. Let’s say we want to simulate the workday of someone washing dishes. Here’s what it could look like:

      # Iterate over each hour of service
for hour in range(1, service_hours + 1):
    print(f"\nHour {hour}:")
    # Call the dishwashing function with the number of dishes for this hour
    wash_dishes(dishes_per_hour)
    

This code snippet uses the range function with a custom offset, namely 1 instead of 0. This is the first parameter of the range function. It simulates an hour of work and it runs for the number of hours in the workday.

Let’s have a look at another common use case. Suppose you have two parallel lists, one with student names and the other with their corresponding scores. If you need to print names with their scores or perform operations using both lists, iterating over indices is effective.

      names = ["Jonas", "Maria", "Adnane", "Patrick"]
scores = [86, 92, 95, 90]

# Print names with their corresponding scores
for i in range(len(names)):
    print(f"{names[i]} scored {scores[i]}")

    

The variables names and scores are two lists where each index in one list corresponds to an index in the other. By using the index i, we access elements from both lists simultaneously, making it easy to pair each name with the corresponding score.

We can also use the for loop to iterate through a dictionary. Chances are you know this, but dictionaries allow you to store data in key-value pairs. This makes it possible to retrieve and manipulate data based on its 'key.' The key is unique within the dictionary. 

Let's consider our school scenario and have the dictionary of students (key) with their grades (value). You want to perform some operations, like updating their grades or printing them out.

      # Dictionary with student names as keys and grades as values
students_grades = {
    "Jonas": 86,
    "Maria": 92,
    "Adnane": 95,
    "Patrick": 90
}
    

With the items() method in the dictionary, we can iterate over all the key-value pairs. In that case, we’ll have to enter two variable names: the first for the key and the second for the value. Here’s how to do that:

      # Printing all student grades
print("List of student grades:")
for name, grade in students_grades.items():
    print(f"{name} scored {grade}")

    

If we can use the key to retrieve the value, we can skip using the items() method and iterate over the dictionary using a basic for loop. We can use the key to access and modify the value.

      # Suppose we need to increase each grade by 10% for a special bonus
print("\nUpdating grades with a 10% bonus:")
for name in students_grades:
    original_grade = students_grades[name]
    new_grade = original_grade * 1.10
    students_grades[name] = new_grade
    print(f"{name}'s new grade: {new_grade:.1f}")

    

These are the fundamentals for iterating through dictionaries. Since a lot of data takes the shape of a collection of key-value pairs, being able to loop through this is important.

Let’s talk about the while loop for a while (pun intended).

The while loop in Python

Now that we’ve got a feel for what iteration and loops are all about, let’s start coding some while loops in Python. The while loop has a simple mantra: Keep going as long as a certain condition is true. 

I’ve a lot of while loops incorporated in my life, and I bet you do, too. A simple one sits right on my desk: my coffee cup. The while loop? "As long as there’s coffee in my cup, I’ll keep sipping." 

And that’s how it works in Python, too. You set up a condition, Python checks the condition, executes the code block, and then keeps executing the block of code underneath it until the condition’s no longer true. It’s structured like this:

      while condition:
  # Execute this block of code

    

Think of it as the loop checking the condition before each iteration, kind of like checking if there's still coffee before taking another sip.

In Python, that would look something like this:

      while check_cup_for_coffee():
  sip()

    

In this case, we call a function to check if there’s still coffee. And if there is, we take a sip. Then we check again. If there’s still coffee (if the function returns “true”), we take another sip. If the function returns “false,” we snap out of the loop and continue with the code below the loop.

Here’s another example to demonstrate how loops work:  

      # Summing numbers from 1 to n
n = 10
total = 0
current_number = 1
while current_number <= n:
    total += current_number
    current_number += 1

print("The sum of numbers from 1 to", n, "is", total)

    

In this example, we sum the numbers from 1 to n and set n to 10. And we keep looping until the current_number is bigger than n. Once that happens, the condition becomes false, and we continue with the code underneath the loop, which prints the sum.

Let’s have a look at some more examples, starting with our job as a dishwasher. Imagine it’s a busy evening, and you need to make sure there’s a continuous flow of clean dishes to serve the guests. We’ll simulate this with a simple while loop and assume we work in batches of dirty dishes. Here’s what it could look like:

      # Number of dirty dishes
dirty_dishes = 10

# Simulate washing dishes
while dirty_dishes > 0:
    print(f"Washing a dish. Dishes left: {dirty_dishes}")
    dirty_dishes -= 1  # One dish is washed

print("All dishes are clean!")

    

This loop continues as long as there are dirty dishes. Each iteration of the loop simulates washing one dish by decrementing the dirty_dishes variable that serves as a counter. We can make this slightly more complicated by dealing with the dirty dishes in batches and taking the capacity of the dishwasher machine into account.

Here’s the process in words: We rinse as many dishes as we have capacity in the dishwasher, then we place them in the dishwasher. Next, we turn the dishwasher on. We repeat these steps for every dish in the batch, and we repeat this for as many batches as there are. This is called a nested loop. 

The nested loop in Python

In some situations, you have to repeat a certain action while repeating another action. And for that, you might need a nested loop (or call a function that uses a loop). Nested loops are also called inner loops. Here’s a basic example of a nested loop:

      # Define a 3x3 grid
grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Outer loop to go through each row
for i in range(len(grid)):
    # Inner loop to go through each column in the current row
    for j in range(len(grid[i])):
        # Access and print the element at position (i, j)
        print(f"Element at ({i}, {j}): {grid[i][j]}")

    

In this example, we have a grid (a list of lists). Each cell of the grid contains a value. We want to print each value with its coordinates in the grid. 

First, we define a simple 3x3 matrix called grid. Each element of the grid can be accessed using two indices: i (the row index) and j (the column index). Next, the outer loop iterates over each row in the grid, where i is the index of the current row. Then for each row, the inner loop iterates over each column, where j is the index of the current column in the row. Inside the inner loop, we access and print the element at the current position (i, j) with its coordinates.

This scenario is ideal for using a nested loop. The outer loop iterates through the rows of the grid, and the inner loop iterates through the columns of each row.

Now back to our dishwasher process. We had: 

      # Total number of dirty dishes
dirty_dishes = 50

# Capacity of the dishwasher
dishwasher_capacity = 10

# Simulate washing dishes in batches
while dirty_dishes > 0:
    if dirty_dishes >= dishwasher_capacity:
        print(f"Rinsing and loading {dishwasher_capacity} dishes into the dishwasher.")
        dirty_dishes -= dishwasher_capacity
    else:
        print(f"Rinsing and loading the last {dirty_dishes} dishes into the dishwasher.")
        dirty_dishes = 0

    # Simulate turning on the dishwasher
    print("Turning on the dishwasher.")
    print("Dishes are being washed.")

print("All dishes are clean!")
    

This actually has a nested loop because we loop through the total load of dirty dishes, grabbing the capacity of the dishwasher each time. But then for every item in that batch, we’ll repeat the rinse process. Here’s what it looks like with a nested loop:

      while dirty_dishes > 0:
    if dirty_dishes >= dishwasher_capacity:
        batch_size = dishwasher_capacity
    else:
        batch_size = dirty_dishes

    # A nested while loop for rinsing each dish in the current batch
    dishes_to_rinse = batch_size
    while dishes_to_rinse > 0:
        print(f"Rinsing dish {batch_size - dishes_to_rinse + 1} of {batch_size}")
        dishes_to_rinse -= 1

    # Decrease the count of dirty dishes after rinsing and loading them
    print(f"Loading {batch_size} dishes into the dishwasher.")
    dirty_dishes -= batch_size

    # Turning on the dishwasher for the current batch
    print("Turning on the dishwasher.")
    print(f"{batch_size} dishes are being washed.")
    

As you can see, our while loop now has an inner loop (nested loop) for every dish we have to rinse manually. We could also add another inner while loop for putting it in the dishwasher after the whole batch is rinsed.

Let's continue with other loop structures and controls in Python. We’ll start with something that people coming from other languages might find strange: having the else keyword after a while loop.

Else after the while loop in Python

You might be surprised that while loops in Python can have an else clause. The else part is executed when the loop terminates naturally. And by naturally, I mean when the condition in the while statement becomes false. It does not execute if the loop is terminated with a break statement (discussed next).

Here’s how it looks:

      count = 0

while count < 5:
    print(f"Count is {count}")
    count += 1
else:
    print("Reached the end of count")
    

This is a simple example, of course. The while loop increments the count until it reaches 5. Once the count is no longer less than 5, the else block runs, indicating that we've "reached the end of count." 

Let’s continue with two general loop control concepts: break and continue.

Break and continue in Python

We can exercise more control on the execution flow of the loop with the keywords “break” and “continue.” 

Let’s start with break. You can stop a loop even if the loop condition has not become false. You do this with a break statement. This is useful when you need to exit based on a specific condition other than the main loop condition. It’s also a common pattern combined with “while true” to avoid an infinite loop.

For example, if we were playing a guessing game and the player guessed the right number, we could stop the game immediately with a break:

      secret_number = 7
guess = 0

while True:
    guess = int(input("Guess the number: "))
    if guess == secret_number:
        print("You guessed it right!")
        break
    print("Try again!")
    

Here we stop the loop with break as soon as the correct number is guessed and the confirmation printed. The code goes on after the loop.

Now let’s find out what continue does in Python. The continue statement doesn’t stop the full loop. Instead, it can skip the rest of the code inside the loop for the current iteration only. It's often used to skip over certain elements.

Let's imagine you want to print only odd numbers up to 10:

      for num in range(10):
    if num % 2 == 0:
        continue
    print(num)
    

Whenever the number is even, it executes the continue statement. This brings the flow back to the top of the loop and continues from there so it skips the print statement. This results in only the odd numbers being printed.

Refactoring the break and continue statement

I’d like to share a few sidenotes about break and continue. Control structures like break and continue can manage the flow within loops. However, often their use is a code smell. A code smell means there’s a problem in the code design. Whenever I feel like I need a break or continue, I wonder how I could add conditions to achieve the same without those keywords.

Why? Because break and continue can make the loop logic harder to understand, especially in more complex loops. Working with conditions can make the code easier to read.

The use of break can be essential when dealing with an infinite loop, as seen in our guessing game example. However, to avoid the code smell, we can refactor the loop to have a natural termination condition that’s a lot more readable and maintains the integrity of the while loop's purpose.

Here’s the original:

      secret_number = 7
guess = 0

while True:
    guess = int(input("Guess the number: "))
    if guess == secret_number:
        print("You guessed it right!")
        break
    print("Try again!")
    

And here’s how we could improve the code and remove break:

      secret_number = 7
guess = None

while guess != secret_number:
    guess = int(input("Guess the number: "))
    if guess != secret_number:
        print("Try again!")
    else:
        print("You guessed it right!")
    

In the improved example, the loop continues until the guess matches the secret_number. We no longer need a break statement, and it’s easier to read what the code does.

We can do another type of improvement for the continue statement: use the continue statement to skip the current iteration of the loop under specific conditions. 

Here’s the original:

      for num in range(10):
    if num % 2 == 0:
        continue
    print(num)
    

And here’s the refactored version without continue:

      for num in range(10):
    if num % 2 != 0:
        print(num)
    

By simply reversing the condition and directly checking for the condition we’re interested in (odd numbers), we no longer need the continue keyword. We can just print the odd numbers right away. 

These refactorings are useful in business environments where code is more complex and maintaining code is expensive—making code as simple as possible is always a good idea. If you do use break and continue, it’s often a good practice to comment why you need it and what you’re doing with that logic. 

The do while loop in Python

A common question I get is how to use the do-while loop in Python. Interestingly, Python does not have a built-in do-while loop. But if you have a C or Java background, you’re familiar with this coding construct. 

In a do-while loop, the body of the loop is executed at least once before the condition is tested. It looks like this in Java:

      do {
  // some code
} while(some condition);
    

It first performs the code in the do block, then it checks the condition. If the condition is true, it performs in the do block again. And it continues until the condition of the loop becomes false.

Python doesn’t have a do while, but there are two ways to achieve the same result:

  • Work with a “while True” loop

  • Create a function with the content of the do block, execute it, and then have the while also calling that function

Here’s an example of the first one using the while True approach:

      while True:
    data = input("Enter some data: ")
    print("You entered:", data)
    if data == "stop":
        break
    

And here’s the second way to do it. (I prefer this option given my somewhat personal dislike of the break statement):

      def process_data():
    data = input("Enter some data: ")
    print("You entered:", data)
    return data

# Perform the action at least once
data = process_data()

# Continue performing the action until the condition becomes false
while data != "stop":
    data = process_data()
    

So while Python doesn’t have a do-while loop, we don’t need the do-while loop to get the same effect. In both these alternatives, the code block is executed at least once, and the condition is checked right after and used to snap out of the loop.

Wrap-up

And that’s it. Loops and iteration are fundamental in Python. We often need to work with iteration over sequences, but we have multiple ways to approach it and multiple ways to solve the same problem. 

You might wonder which one to use when. You should use for loops for definite iterations and while loops for conditions that need regular checking. 

A last quick tip to optimize loop performance: Avoid unnecessary computations or heavy operations, such as creating connections or calling APIs, inside the loop when you can do it  outside the loop.


Further learning with Python

To boost your Python learning journey, Pluralsight has numerous resources available, including online tutorials, courses, and hands-on labs where you get practical experience. The best place to start is Pluralsight's Python 3 learning path, which has beginner, intermediate, and advanced courses, and an online skill test you can use to test your current Python proficiency.

Additionally, you can check out the following articles in our ongoing Python tutorial series:


Interested in contributing to the Pluralsight community?

If you’ve got an article you want to write, we want to hear from you! Visit our community contribution page and register your interest.

Maaike van Putten

Maaike v.

Maaike is a trainer and software developer. She founded training agency Brightboost in 2014 and spends most of her days and nights working and learning. Training gives her the opportunity to combine her love for software development with her passion to help others boost their careers and be successful. She has trained professionals in the field of Java, Spring, C#, Python, Scrum, React and Angular. A lot of her time is spend staying up-to-date with the latest developments in her field.

More about this author