Python for Loops: The Pythonic Way

Python for Loops: The Pythonic Way

by Leodanis Pozo Ramos Feb 03, 2025 intermediate best-practices python

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: For Loops in Python (Definite Iteration)

Python’s for loop allows you to iterate over the items in a collection, such as lists, tuples, strings, and dictionaries. The for loop syntax declares a loop variable that takes each item from the collection in each iteration. This loop is ideal for repeatedly executing a block of code on each item in the collection. You can also tweak for loops further with features like break, continue, and else.

By the end of this tutorial, you’ll understand that:

  • Python’s for loop iterates over items in a data collection, allowing you to execute code for each item.
  • To iterate from 0 to 10, you use the for index in range(11): construct.
  • To repeat code a number of times without processing the data of an iterable, use the for _ in range(times): construct.
  • To do index-based iteration, you can use for index, value in enumerate(iterable): to access both index and item.

In this tutorial, you’ll gain practical knowledge of using for loops to traverse various collections and learn Pythonic looping techniques. Additionally, you’ll learn how to handle exceptions and how to use asynchronous iterations to make your Python code more robust and efficient.

Take the Quiz: Test your knowledge with our interactive “The Python for Loop” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

The Python for Loop

In this quiz, you'll test your understanding of Python's for loop and the concepts of definite iteration, iterables, and iterators. With this knowledge, you'll be able to perform repetitive tasks in Python more efficiently.

Getting Started With the Python for Loop

In programming, loops are control flow statements that allow you to repeat a given set of operations a number of times. In practice, you’ll find two main types of loops:

  1. for loops are mostly used to iterate a known number of times, which is common when you’re processing data collections with a specific number of data items.
  2. while loops are commonly used to iterate an unknown number of times, which is useful when the number of iterations depends on a given condition.

Python has both of these loops and in this tutorial, you’ll learn about for loops. In Python, you’ll generally use for loops when you need to iterate over the items in a data collection. This type of loop lets you traverse different data collections and run a specific group of statements on or with each item in the input collection.

In Python, for loops are compound statements with a header and a code block that runs a predefined number of times. The basic syntax of a for loop is shown below:

Python Syntax
for variable in iterable:
    <body>

In this syntax, variable is the loop variable. In each iteration, this variable takes the value of the current item in iterable, which represents the data collection you need to iterate over. The loop body can consist of one or more statements that must be indented properly.

Here’s a more detailed breakdown of this syntax:

  • for is the keyword that initiates the loop header.
  • variable is a variable that holds the current item in the input iterable.
  • in is a keyword that connects the loop variable with the iterable.
  • iterable is a data collection that can be iterated over.
  • <body> consists of one or more statements to execute in each iteration.

Here’s a quick example of how you can use a for loop to iterate over a list:

Python
>>> colors = ["red", "green", "blue", "yellow"]

>>> for color in colors:
...     print(color)
...
red
green
blue
yellow

In this example, color is the loop variable, while the colors list is the target collection. Each time through the loop, color takes on a successive item from colors. In this loop, the body consists of a call to print() that displays the value on the screen. This loop runs once for each item in the target iterable. The way the code above is written is the Pythonic way to write it.

However, what’s an iterable anyway? In Python, an iterable is an object—often a data collection—that can be iterated over. Common examples of iterables in Python include lists, tuples, strings, dictionaries, and sets, which are all built-in data types. You can also have custom classes that support iteration.

You can also have a loop with multiple loop variables:

Python
>>> points = [(1, 4), (3, 6), (7, 3)]

>>> for x, y in points:
...     print(f"{x = } and {y = }")
...
x = 1 and y = 4
x = 3 and y = 6
x = 7 and y = 3

In this loop, you have two loop variables, x and y. Note that to use this syntax, you just need to provide a tuple of loop variables. Also, you can have as many loop variables as you need as long as you have the correct number of items to unpack into them. You’ll also find this pattern useful when iterating over dictionary items or when you need to do parallel iteration.

Sometimes, the input iterable may be empty. In that case, the loop will run its header once but won’t execute its body:

Python
>>> for item in []:
...     print(item)
...

In this example, the target iterable is an empty list. The loop checks whether the iterable has items. If that’s the case, then the loop runs once for each item. If the iterable has no items, then the loop body doesn’t run, and the program’s execution flow jumps onto the statement after the loop.

Now that you know the basic syntax of for loops, it’s time to dive into some practical examples. In the following section, you’ll learn how to use for loops with the most common built-in data collections in Python.

Traversing Built-in Collections in Python

When writing Python code, you’ll often need to iterate over built-in data types such as lists, tuples, strings, numeric ranges, dictionaries, and sets. All of them support iteration, and you can feed them into a for loop. In the next sections, you’ll learn how to tackle this requirement in a Pythonic way.

Sequences: Lists, Tuples, Strings, and Ranges

When it comes to iterating over sequence data types like lists, tuples, strings, and ranges, the iteration happens in the same order that the items appear in the sequence. Consider the following example where you iterate over the numbers in a list:

Python
>>> numbers = [1, 2, 3, 4]

>>> for number in numbers:
...     print(number)
...
1
2
3
4

In this example, the iteration goes through the list in the definition order, starting with 1 and ending with 4. Note that to iterate over a sequence in Python, you don’t need to be aware of the index of each item as in other languages where loops often rely on indices.

Often, you use plural nouns to name lists. This naming practice allows you to use singular nouns as the loop variable, making your code descriptive and readable.

You’ll note the same behavior with other built-in sequences:

Python
>>> person = ("Jane", 25, "Python Dev", "Canada")
>>> for field in person:
...     print(field)
...
Jane
25
Python Dev
Canada

>>> text = "abcde"
>>> for character in text:
...     print(character)
...
a
b
c
d
e

>>> for index in range(5):
...     print(index)
...
0
1
2
3
4

In these examples, you iterate over a tuple, string, and numeric range. Again, the loop traverses the sequence in the order of definition.

Tuples are often used to represent rows of data. In the example above, the person tuple holds data about a person. You can iterate over each field using a readable loop.

When it comes to iterating over string objects, the for loop lets you process the string on a character-by-character basis. Finally, iterating over a numeric range is sometimes a requirement, especially when you need to iterate a given number of times and need control over the consecutive index.

Collections: Dictionaries and Sets

When traversing dictionaries with a for loop, you’ll find that you can iterate over the keys, values, and items of the dictionary at hand.

You’ll have two different ways to iterate over the keys of a dictionary. You can either use:

  1. The dictionary directly
  2. The .keys() method

The following examples show how to use these two approaches:

Python
>>> students = {
...     "Alice": 89.5,
...     "Bob": 76.0,
...     "Charlie": 92.3,
...     "Diana": 84.7,
...     "Ethan": 88.9,
... }

>>> for student in students:
...     print(student)
...
Alice
Bob
Charlie
Diana
Ethan

>>> for student in students.keys():
...     print(student)
...
Alice
Bob
Charlie
Diana
Ethan

In these examples, you first iterate over the keys of a dictionary using the dictionary directly in the loop header. In the second loop, you use the .keys() method to iterate over the keys. While both approaches are equivalent, the first one is more commonly used, whereas the second might be more readable and explicit.

In both loops, you can access the dictionary values using the keys:

Python
>>> for student in students:
...     print(student, "->", students[student])
...
Alice -> 89.5
Bob -> 76.0
Charlie -> 92.3
Diana -> 84.7
Ethan -> 88.9

To access the values in this type of iteration, you can use the original dictionary and a key lookup operation, as shown in the highlighted line.

You can use the .values() method to feed the for loop when you need to iterate over the values of a dictionary:

Python
>>> teams = {
...     "Colorado": "Rockies",
...     "Chicago": "White Sox",
...     "Boston": "Red Sox",
...     "Minnesota": "Twins",
...     "Milwaukee": "Brewers",
...     "Seattle": "Mariners",
... }

>>> for team in teams.values():
...     print(team)
...
Rockies
White Sox
Red Sox
Twins
Brewers
Mariners

The .values() method lets you traverse the values in the target dictionary. In this example, you iterate over team names one by one. Note that when you use the .values() method, you can’t access the dictionary keys.

Finally, iterating over both keys and values in a Python dictionary is a common requirement. In this case, the recommended and most Pythonic approach is to use the .items() method in a for loop like the following:

Python
>>> for place, team in teams.items():
...     print(place, "->", team)
...
Colorado -> Rockies
Chicago -> White Sox
Boston -> Red Sox
Minnesota -> Twins
Milwaukee -> Brewers
Seattle -> Mariners

When iterating over keys and values this way, you typically use a tuple of loop variables. The first variable will get the key, while the second will get the associated value. In this example, you have the place and team variables, which make the code clear and readable.

When it comes to iterating over sets, you only have to keep in mind that sets are unordered data types. This means that looping in order isn’t guaranteed:

Python
>>> tools = {"Django", "Flask", "pandas", "NumPy"}

>>> for tool in tools:
...     print(tool)
...
NumPy
Flask
pandas
Django

As you can see, the loop goes through the elements of your set in a different order than they were inserted. So, you can’t rely on the order of the elements when traversing sets in Python.

Using Advanced for Loop Syntax

The Python for loop has some advanced features that make it flexible and powerful. These features can be helpful when you need to fine-tune the loop to meet specific execution flows. These features include the break and continue statements and the else clause, which you’ll learn about in the following sections.

You’ll also learn that for loops can be nested inside one another. This feature can be pretty useful in situations where you need to iterate over nested data structures like lists of lists.

The break Statement

The break statement immediately exits the loop and jumps to the first statement after the loop. For example, say that you want to write a loop to determine whether a number is in a list. To avoid unnecessary work, the loop should terminate once it finds the target value. You can do this with the break statement:

Python
>>> numbers = [1, 3, 5, 7, 9]
>>> target = 5

>>> for number in numbers:
...     print(f"Processing {number}...")
...     if number == target:
...         print(f"Target found {target}!")
...         break
...
Processing 1...
Processing 3...
Processing 5...
Target found 5!

In this example, the break statement jumps out of the loop as soon as the target number is found. The remaining values, 7 and 9, aren’t processed. You can think of the break statement as a way to short-circuit the loop execution once you’ve gotten the desired result.

It’s important to note that it makes little sense to have break statements outside conditionals. Suppose you include a break statement directly in the loop body without wrapping it in a conditional. In that case, the loop will terminate in the first iteration, potentially without running the entire loop body.

The continue Statement

The continue statement terminates the current iteration and proceeds to the next one. For example, if you have a list of numbers and only want to process the even ones, you can use a continue statement to skip the odd numbers:

Python
>>> numbers = [1, 2, 3, 4, 5, 6]

>>> for number in numbers:
...     print(f"{number = }")
...     if number % 2 != 0:
...         continue
...     print(f"{number} is even!")
...
number = 1
number = 2
2 is even!
number = 3
number = 4
4 is even!
number = 5
number = 6
6 is even!

In this example, the code that processes the numbers is only reached if the number is even. Otherwise, the continue statement skips that code and jumps right into the next iteration.

Again, it doesn’t make much sense to have a continue statement without wrapping it in a conditional. If you do so, the code after the statement will be unreachable and never run.

The else Clause

In Python, for loops can have an else clause at the end. The else clause will only run if the loop terminates because of the exhaustion of the input iterable. This feature is useful when you have a break statement that can terminate the loop in certain situations. If the loop doesn’t break, then you can run additional code in the else clause.

To illustrate, say that you want to continue improving the loop that determines whether a number is in a list. You’d like to explicitly inform the user if the number isn’t in the list. You can do this with the else clause:

Python
>>> numbers = [1, 3, 5, 7, 9]
>>> target = 42

>>> for number in numbers:
...     print(f"Processing {number}...")
...     if number == target:
...         print(f"Target found {target}!")
...         break
... else:
...     print(f"Target not found {target}")
...
Processing 1...
Processing 3...
Processing 5...
Processing 7...
Processing 9...
Target not found 42

The else clause won’t run if the loop breaks out with the break statement. It only runs if the loop terminates normally, allowing you to inform the user that the target number wasn’t found.

It doesn’t make sense to have an else clause in a loop that doesn’t have a break statement. In that case, placing the else block’s content after the loop—without indentation—will work the same and be cleaner.

Nested for Loops

You can also have nested for loops. In the example below, you create a multiplication table that shows the products of all combinations of integers up to ten using nested loops. The outer loop iterates over the numbers between 1 and 10, and the inner loop calculates and prints the products:

Python
>>> for number in range(1, 11):
...     for product in range(number, number * 11, number):
...         print(f"{product:>4d}", end="")
...     print()
...
   1   2   3   4   5   6   7   8   9  10
   2   4   6   8  10  12  14  16  18  20
   3   6   9  12  15  18  21  24  27  30
   4   8  12  16  20  24  28  32  36  40
   5  10  15  20  25  30  35  40  45  50
   6  12  18  24  30  36  42  48  54  60
   7  14  21  28  35  42  49  56  63  70
   8  16  24  32  40  48  56  64  72  80
   9  18  27  36  45  54  63  72  81  90
  10  20  30  40  50  60  70  80  90 100

In this example, you use two nested loops. Together, they create a two-dimensional multiplication table. First, you loop over the numbers from one up to and including ten. These represent the rows in the table, and you can see those numbers at the beginning of each row.

In the inner loop, you calculate the products for the current number by iterating from the number itself up to its tenth multiple. Then, you format each product using the :>4d format specifier. This ensures the table is nicely aligned. By setting end to an empty string, you skip the newline until the products on the current row are printed. After printing all products for a row, you use print() without arguments to move to the next row.

Exploring Pythonic Looping Techniques

When people switch from other programming languages to Python, they often write for loops like they did in their previous language. This practice makes Python code look odd and hard to read.

In the following sections, you’ll explore some looping techniques, practices, and tips that are considered Pythonic. These techniques can make your Python code look clearer, more elegant, and more efficient.

Iterating With Indices: The Pythonic Way

Sometimes, you need to use the indices of items when you iterate over a sequence with a Python for loop. Up to this point, you’ve seen examples where you can access the items but don’t know their corresponding indices.

To get both the item and its index, you can end up writing a loop like the one shown in the following example:

Python
>>> fruits = ["orange", "apple", "mango", "lemon"]

>>> for index in range(len(fruits)):
...     fruit = fruits[index]
...     print(index, fruit)
...
0 orange
1 apple
2 mango
3 lemon

This loop gets the job done, but it’s not as clean or readable as you’d expect from Python code. Fortunately, there’s a better way—the built-in enumerate() function:

Python
>>> for index, fruit in enumerate(fruits):
...     print(index, fruit)
...
0 orange
1 apple
2 mango
3 lemon

The enumerate() function takes an iterable as an argument and generates tuples of the form (index, item). Note that the loop reads almost like plain English, which makes your code way more Pythonic than the previous version using range().

The enumerate() function also takes an optional argument called start that lets you tweak the initial value. This feature is useful when you need to create counts. Consider the following example that mimics an option menu for a command-line application:

Python
>>> def display_menu(options):
...     print("Main Menu:")
...     for position, option in enumerate(options, start=1):
...         print(f"{position}. {option}")
...

>>> display_menu(["Open", "Save", "Settings", "Quit"])
Main Menu:
1. Open
2. Save
3. Settings
4. Quit

In this example, instead of using enumerate() to produce zero-based indices, you start the count at 1. From the end user’s perspective, starting the menu at 1 is the natural way to go.

Looping Over Several Iterables in Parallel

Looping through two or more iterables in parallel may be another common task you encounter in Python programming. To do this, you can use the built-in zip() function, which takes two or more iterables and yields tuples that combine items from each iterable.

Consider the following toy example:

Python
>>> numbers = [1, 2, 3]
>>> letters = ["a", "b", "c"]

>>> for number, letter in zip(numbers, letters):
...     print(number, "->", letter)
...
1 -> a
2 -> b
3 -> c

In this example, you use zip(numbers, letters) to create an iterator that produces tuples of the form (number, letter). In this case, the number values are taken from numbers, and the letter values are taken from letters.

Iterating Over Multiple Iterables Sequentially

There may be times when you need to iterate over multiple iterables sequentially in a single loop. In such cases, you can use the chain() function from Python’s itertools module.

For example, say that you have several lists of numbers and want to calculate the square of each number in all lists. You can use chain() as follows:

Python
>>> from itertools import chain

>>> first = [7, 6, 1]
>>> second = [4, 1]
>>> third = [8, 0, 6]

>>> for value in chain(first, second, third):
...     print(value**2)
...
49
36
1
16
1
64
0
36

This loops over all three lists in sequence and prints the square of each value. You can also use chain() to work through a list of lists. Say that you, again, need to process each value in a sequence and calculate its square:

Python
>>> matrix = [
...     [9, 3, 8],
...     [4, 5, 2],
...     [6, 4, 3],
... ]

>>> for value in chain(*matrix):
...     print(value**2)
...
81
9
64
16
25
4
36
16
9

In this example, you use chain() to iterate over the rows of the matrix. To feed the rows into chain(), you use the unpacking operator (*). Inside the loop, you calculate and print the square of each value.

Using chain(), like in this example, essentially flattens the matrix into a single iterable, helping you avoid a nested loop, which can be difficult to read and understand in some contexts.

Repeating Actions a Predefined Number of Times

Iteration is all about repeating some fragment of code multiple times. As you’ve learned so far, for loops are designed to repeat a given set of actions on the items of an iterable. However, you can also use this type of loop to quickly iterate a specific number of times. This is useful when you need to repeat a bunch of statements, but they don’t operate on the items of an iterable.

Here’s a fun example about Penny and Sheldon to illustrate this:

Python
>>> for _ in range(3):
...     print("Knock, knock, knock")
...     print("Penny!")
...
Knock, knock, knock
Penny!
Knock, knock, knock
Penny!
Knock, knock, knock
Penny!

This loop runs three times and repeats a series of statements that don’t operate on any iterable. Note that the loop variable is a single underscore character in this example. This variable name communicates that you don’t need to use the loop variable inside the loop. It’s a throwaway variable.

With this looping construct that takes advantage of range(), you have full control over the number of times your code runs.

Iterating Over Reversed and Sorted Iterables

Iterating over the items of an iterable in reverse or sorted order is also a common requirement in programming. To achieve this, you can combine a for loop with the built-in reversed() or sorted() function, respectively.

For example, say that you’re working on a text editor and want to implement a basic Undo option. You can implement it with the reversed() function and a loop like the following:

Python
>>> actions = ["Type text", "Select text", "Cut text", "Paste text"]

>>> for action in reversed(actions):
...     print(f"Undo: {action}")
...
Undo: Paste text
Undo: Cut text
Undo: Select text
Undo: Type text

In this example, you have a list of hypothetical user actions in a text editor. The actions are stored in a list from oldest to newest. To implement the Undo operation, you need to reverse the actions, which you do with reversed().

To iterate in sorted order, say that you have a dictionary that maps student names to their corresponding average grades. You need to create a quick report and want to sort the data from highest to lowest grades. For this, you can do something like the following:

Python
>>> students = {
...     "Alice": 89.5,
...     "Bob": 76.0,
...     "Charlie": 92.3,
...     "Diana": 84.7,
...     "Ethan": 88.9,
...     "Fiona": 95.6,
...     "George": 73.4,
...     "Hannah": 81.2,
... }

>>> sorted_students = sorted(
...     students.items(), key=lambda item: item[1], reverse=True
... )

>>> for name, grade in sorted_students:
...     print(f"{name}'s average grade: {grade:->{20-len(name)}.1f}")
...
Fiona's average grade: -----------95.6
Charlie's average grade: ---------92.3
Alice's average grade: -----------89.5
Ethan's average grade: -----------88.9
Diana's average grade: -----------84.7
Hannah's average grade: ----------81.2
Bob's average grade: -------------76.0
George's average grade: ----------73.4

The sorted() function returns a list of sorted values. In this example, you sort the dictionary by its values in ascending order. To do this, you use a lambda function that takes a two-value tuple as an argument and returns the second item, which has an index of 1. You also set the reverse argument to True so that the function stores the data in reverse order. In this case, this means that the grades are ordered in descending order.

The for loop iterates over the sorted data and generates a nicely formatted report using an f-string with a custom format specifier.

Understanding Common Pitfalls in for Loops

When working with for loops in your Python code, you may encounter some issues related to incorrect ways to use this tool. Some of the most common bad practices and incorrect assumptions include:

  • Modifying the loop collection or iterable during iteration
  • Changing the loop variable to affect the underlying collection
  • Ignoring possible exceptions that may occur

In the following sections, you’ll explore these pitfalls and how to avoid them in your for loops.

Modifying the Loop Collection

Python has mutable collections, such as lists and dictionaries, that you can modify in place. You may want to change a list while looping over it. In this situation, you need to distinguish between safe and unsafe changes.

For example, say that you have a list of names and want to convert them into uppercase. You may think of doing something like the following:

Python
>>> names = ["Alice", "Bob", "John", "Jane"]

>>> for index, name in enumerate(names):
...     names[index] = name.upper()
...

>>> names
['ALICE', 'BOB', 'JOHN', 'JANE']

In this example, you only change the existing items in the list without adding or removing any. This operation is safe. However, modifying a mutable iterable like a list while iterating over it always raises a warning.

Issues may appear when you add or remove items from a list while iterating over it. To understand why this is best avoided, say that you want to remove all the even numbers from a list. You might write the following code:

Python
>>> numbers = [2, 4, 6, 8]

>>> for number in numbers:
...     if number % 2 == 0:
...         numbers.remove(number)
...

>>> numbers
[4, 8]

After running the loop, some even numbers remain, even though you expected the list to be empty.

On the first iteration, 2 is removed, and the list shifts left, becoming [4, 6, 8]. The loop then jumps to the next item, skipping 4 and processing 6 instead. Then 6 is removed, and the list shifts again, becoming [4, 8]. The iteration ends before reaching 8.

When you need to resize a list during iteration like in the example above, it’s recommended to create a copy of the list:

Python
>>> numbers = [2, 4, 6, 8]

>>> for number in numbers[:]:
...     if number % 2 == 0:
...         numbers.remove(number)
...
>>> numbers
[]

The slicing operator ([:]) with no indices creates a copy of the original list for iteration purposes. The loop traverses the copy while removing values from the original list.

In some cases, creating a copy of the input list isn’t enough. Say that on top of removing even numbers, you want to calculate the square of odd numbers. You might modify the previous loop as shown in the following code:

Python
>>> numbers = [2, 1, 4, 6, 5, 8]

>>> for index, number in enumerate(numbers[:]):
...     if number % 2 == 0:
...         numbers.remove(number)
...     else:
...         numbers[index] = number**2
...
Traceback (most recent call last):
    ...
ValueError: list.remove(x): x not in list

This time, you use enumerate() to generate index-item pairs. Then, you think of using the index to update the value of a given item. However, the code fails with a ValueError exception. Creating a copy of the input list isn’t enough in this case. You’d have to make a separate list to store the result:

Python
>>> numbers = [2, 1, 4, 6, 5, 8]
>>> processed_numbers = []

>>> for number in numbers:
...     if number % 2 != 0:
...         processed_numbers.append(number**2)
...

>>> processed_numbers
[1, 25]

In this new loop implementation, you’re using a new list to store the result. Because of this, you don’t have to remove items anymore. You add the square values to the end of the new list using the .append() method.

Python doesn’t allow you to add or remove items from a dictionary while you’re iterating through it:

Python
>>> values = {"one": 1, "two": 2, "three": 3, "four": 4}

>>> for value in values:
...     values["five"] = 5  # Attempt to add an item
...
Traceback (most recent call last):
    ...
RuntimeError: dictionary changed size during iteration

>>> for value in values:
...     del values[value]  # Attempt to remove an item
...
Traceback (most recent call last):
    ...
RuntimeError: dictionary changed size during iteration

If you try to expand or shrink a dictionary during iteration, you get a RuntimeError exception. Again, you can work around this by creating a copy of the dictionary using the .copy() method or by building a new dictionary with the resulting data.

Changing the Loop Variable

Changing the loop variable in the loop body doesn’t have an effect on the original data:

Python
>>> names = ["Alice", "Bob", "John", "Jane"]

>>> for name in names:
...     name = name.upper()
...     print(name)
...
ALICE
BOB
JOHN
JANME

>>> names
['Alice', 'Bob', 'John', 'Jane']

In this example, the highlighted line changes the loop variable, name. This change doesn’t affect the original data in your list of names. The loop variable is just a temporary reference to the current item in the iterable, and reassigning it doesn’t affect the loop iterable.

Ignoring Possible Exceptions

If an exception occurs in a loop body and isn’t handled, the loop will terminate prematurely, skipping subsequent iterations. This result can generate unexpected issues, especially when you rely on the loop to process data, perform logging, or run cleanup actions in each iteration.

As an example, say that you want to process some text files in a loop:

Python
>>> files = ["file1.txt", "file2.txt", "file3.txt"]

>>> for file in files:
...     with open(file, "r") as f:
...         print(f"Contents of {file}:")
...         print(f.read())
...
Traceback (most recent call last):
    ...
FileNotFoundError: [Errno 2] No such file or directory: 'file1.txt'

In this example, none of the files exist in your working directory. The loop tries to process the first file and fails with a FileNotFoundError exception. Because the exception wasn’t handled properly, the loop terminates in the first iteration, skipping the rest of the files in the list.

To avoid this behavior, you need to catch and handle the exception:

Python
>>> files = ["file1.txt", "file2.txt", "file3.txt"]

>>> for file in files:
...     try:
...         with open(file, "r") as f:
...             print(f"Contents of {file}:")
...             print(f.read())
...     except FileNotFoundError:
...         print(f"Error: {file} not found. Skipping.")
...
Error: file1.txt not found. Skipping.
Error: file2.txt not found. Skipping.
Error: file3.txt not found. Skipping.

In this new implementation, the loop catches any FileNotFoundError exception and prints an error message to the screen. The loop runs entirely without abrupt interruptions.

Using for Loops vs Comprehensions

When you use for loops to transform data and build new collections, it may be possible to replace the loop with a comprehension. For example, consider the loop below:

Python
>>> cubes = []

>>> for number in range(10):
...     cubes.append(number**3)
...

>>> cubes
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]

In this example, you first define an empty list called cubes. Then, you use a loop to iterate over a range of integer numbers and populate the list with cube values.

You can quickly replace the above loop with a list comprehension like the following:

Python
>>> cubes = [number**3 for number in range(10)]

>>> cubes
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]

The comprehension iterates over the range of numbers and builds the list of cubes in a single line of code.

Using async for Loops for Asynchronous Iteration

The async for statement allows you to create loops that iterate over asynchronous iterables. This type of loop works pretty much the same as regular for loops, but the loop collection must be an asynchronous iterator or iterable.

The example below shows an AsyncRange class that generates ranges of integer values asynchronously. You can use this iterable in an async for loop:

Python async_range.py
import asyncio

class AsyncRange:
    def __init__(self, start, end):
        self.data = range(start, end)

    async def __aiter__(self):
        for index in self.data:
            await asyncio.sleep(0.5)
            yield index

async def main():
    async for index in AsyncRange(0, 5):
        print(index)

asyncio.run(main())

In this code, the loop in the highlighted line iterates over integer indices from 0 to 5 in an asynchronous manner.

When you run this script, you get the following output:

Shell
$ python async_range.py
0
1
2
3
4

In this output, each number is obtained after waiting half a second, which is consistent with the asynchronous iteration.

Conclusion

You’ve learned how to use Python for loops to iterate over various data collections, including lists, tuples, strings, dictionaries, and sets. You’ve explored advanced loop features like the break and continue statements, the else clause, and nested loops. Additionally, you learned about Pythonic looping techniques, common pitfalls, and the use of async for loops for asynchronous iteration.

Understanding for loops is essential for Python developers, as they offer an efficient way to manage repetitive tasks and process data. Mastering for loops helps you write code that is more Pythonic, performant, and easier to maintain.

In this tutorial, you’ve learned how to:

  • Iterate over different Python collections using for loops
  • Use advanced features like break, continue, and else in loops
  • Apply Pythonic techniques for cleaner and more efficient loops
  • Work around common pitfalls when working with loops
  • Use async for loops for asynchronous data processing

With these skills, you can now write more efficient and readable Python code that leverages the power of for loops to handle a wide range of data processing tasks.

Frequently Asked Questions

Now that you have some experience with Python for loops, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click the Show/Hide toggle beside each question to reveal the answer.

You use a for loop to iterate over a list by specifying the loop variable and the list. For example, for item in a_list: allows you to process each item in a_list.

An iterable is an object capable of returning its members one at a time, while an iterator is an object that represents a stream of data, returning the next item with a .__next__() special method.

You can iterate over both keys and values in a dictionary using the .items() method in a for loop, like for key, value in a_dict.items():.

If you modify a list while iterating over it, you may encounter unexpected behavior, such as skipping elements or runtime errors. It’s recommended to iterate over a copy of the list instead. In some cases, it’s necessary to create a new list to keep the result.

To handle exceptions in a for loop, wrap the code that might raise an exception in a try block. Then use an except block to catch the exception, manage the error, and continue the iteration.

Take the Quiz: Test your knowledge with our interactive “The Python for Loop” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

The Python for Loop

In this quiz, you'll test your understanding of Python's for loop and the concepts of definite iteration, iterables, and iterators. With this knowledge, you'll be able to perform repetitive tasks in Python more efficiently.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: For Loops in Python (Definite Iteration)

🐍 Python Tricks 💌

Get a short & sweet Python 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

About Leodanis Pozo Ramos

Leodanis is an industrial engineer who loves Python and software development. He's a self-taught Python developer with 6+ years of experience. He's an avid technical writer with a growing number of articles published on Real Python and other sites.

» More about Leodanis

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:

Master Real-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 »

Master Real-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 and get answers to common questions in our support portal.


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