How to use for loops in Python: a practical guide
The for loop in Python is used to repeatedly execute a block of code. For loops are a fundamental part of most programming languages. Keep reading to find out how the for loop works in Python and how to use it.
- Free website protection with SSL Wildcard included
- Free private registration for greater privacy
- Free 2 GB email account
What is the for loop in Python?
The for loop is one of the most well-known programming constructs. Let’s look at it using a concrete example from everyday life. Say that a teacher wants to calculate the average height of her students. First she’ll ask each student in turn what their height is and add each student’s height to the running total. Once she’s done that for every student, she’ll divide the total by the number of students there are to get the average height of her students. We can build a basic algorithm based on the teacher’s actions:
- Ask for the height of each student and add it to the running total.
- Divide the total by the total number of students.
The first step will be executed once for each student and doesn’t require knowing the number of students in the class. The second step is only executed once, no matter how many students are in the class. We’ll need a loop for the first step.
Here’s how the whole algorithm would look in Python, including a for loop:
student_heights = [60, 65, 56, 71, 58, 63, 67]
height_sum = 0
for height in student_heights:
height_sum = height_sum + height
average_height = height_sum // len(student_heights)
print(average_height)
So, the Python for loop repeatedly executes a block of code. This is also referred to as 'iteration'. Loops make it possible to repeat a process for several elements of a sequence. For loops are used in Python when the size of a sequence can be determined at program runtime. Otherwise, a Python while loop is usually used.
Learn how to program in Python with our Python tutorial!
What’s the difference between the for loop in Python and other languages?
Many other programming languages implement for loops in some way. They’re fundamental to languages like C, Java, JavaScript and PHP. Purely functional programming languages like Haskell and Lisp usually don’t use explicit for loops. They use recursive functions instead of iterations.
Every loop involves repeatedly executing a block of code. However, the for loop in Python works fundamentally differently than for loops in other languages. Most programming languages use a loop variable, which is incremented or decremented with each successful iteration of the loop.
Operation | Meaning | Typical syntax | Python syntax |
---|---|---|---|
Increment | Increase the value of an integer variable by a specific, fixed amount | i++ | index += 1 |
Decrement | Decrease the value of an integer variable by a specific, fixed amount | i-- | index -= 1 |
To see how this makes a difference, let’s take a look at an example of how for loops work in other languages. We’ll output the numbers 0 to 9 in JavaScript with a for loop. To do this, we’ll define an integer variable called 'number' and increment it if its value is less than 10. The code for this in JavaScript will probably look incomprehensible to beginners:
for ( let number = 0; number < 10; number++ ) {
console.log(number);
}
On the other hand, the code for the same for loop in Python is significantly easier to follow:
for number in range(10):
print(number)
Rather than directly outputting the loop variable, one element from a collection is usually used for indexing. Let’s look at another example from JavaScript, in which we output the names from the list 'people'. We’ll use the loop variable 'i' as the running index of the individual elements:
people = ['Jack', 'Jim', 'John']
for (let i = 0; i < people.length; i++) {
console.log("Here comes " + people[i]);
}
Be careful about directly indexing successive elements in a list. If the loop is executed one too many times, you’ll end up with a runtime error, often the infamous 'off-by-one error'. Python does away with this problem. Below we show the same case implemented in Python. We iterate over the list elements without indexing them using a loop variable:
people = ['Jack', 'Jim', 'John']
for person in people:
print(f"Here comes {person}")
The indirect use of loop variables leads to a lot of confusion for people learning about for loops in languages other than Python. The loop variable is at the centre of the code, even though it usually doesn’t play a crucial role — its only purpose is to index the individual elements. Anyone hoping to use typical for loops will need to understand several other complex topics. For example, in JavaScript:
Topic | In JavaScript for loop |
---|---|
Assigning variable | let i = 0 |
Boolean expressions | i < limit |
Increment/decrement operator | i++ / i-- |
Determining size of list | i < list.length |
Zero-based indexing of elements | i < list.length is ok; i <= list.length results in off-by-one error |
The for loop in Python is significantly more user friendly. Despite its use of the same word 'for', it takes a fundamentally different approach. Instead of incrementing a loop variable and indexing successive elements, it iterates directly over the elements in a sequence. The for loop in Python is comparable to the forEach construct found in languages like Ruby and JavaScript.
For example, say we want to output the individual letters of a word. One letter will be made available via the variable 'letter' for each iteration of the loop. It does without a loop variable, meaning that there’s no risk of an off-by-one error. The code is precise and easy to read:
word = "Python"
for letter in word:
print(letter)
How does the for loop work in Python?
As we’ve seen, the Python for loop elegantly solves the problem of iterating over the elements in a list. The key is leaving out the loop variable. But how does that work? To understand how the Python for loop works, you’ll first need to know a bit about iterables and iterators.
What are iterables, iterators and generators?
In Python, the for loop operates on objects known as 'iterables'. This includes strings, lists, tuples and other collections of data. In the words of the official Python documentation:
“[An iterable is] an object capable of returning its members one at a time” — Source: https://docs.python.org/3/glossary.html#term-iterable
An iterable object has two properties:
- It brings together multiple elements as a collection.
- It provides access to the elements via an iterator.
Bringing these two points together, we can say that iterables are collections whose contents can be iterated over. Specifically, iterables have a method '__iter__()' that generates an iterator. An iterator is an object that can return the next element of an iterable on command. An iterator also remembers the position of the last returned object form the collection.
Function | Explanation | Example |
---|---|---|
iter(collection) | Calls the __iter__() method of the collection | it = iter("Python") |
next(iter) | Calls the __next__() method of the iterator | next(it) |
collection[index] | Calls the __getitem__(index) method of the collection | 'Python'[1] |
When the method __next__() is called, the iterator returns the next element. The iterator is exhausted when all of the elements in the collection have been returned. Calling __next__() again will raise the 'StopIteration' exception.
Let’s take a look at how this all works in practice using an example. We’ll create a range() object that represents the consecutive numbers from 21 to 23. Then we’ll create an iterator with the iter() function and return successive elements with the next() function. The exception will be raised on the last call, since the iterator is exhausted:
numbers = range(21, 24)
number = iter(numbers)
next(number)
# returns `21`
next(number)
# returns `22`
next(number)
# returns `23`
next(number)
# raises `StopIteration` exception
The iterator provides access to the individual elements in a collection. In addition, there’s the related concept of the generator. Unlike an iterator, a generator creates the individual elements at the time of access. This use of 'lazy execution' saves memory.
Generators in Python are functions that contain at least one yield statement. Similar to the return statement, the yield statement returns an object and ends the call. However, unlike the return statement, the yield statement will pick up where it left off on successive calls.
Let’s look at an example. We’ll write our own implementation of the range() function using a yield statement in the while loop to generate continuous numbers:
def my_range(start, stop):
if stop < start:
return None
current = start
while current < stop:
yield current
current += 1
# test
assert list(my_range(7, 9)) == list(range(7, 9))
Skipping iterations and terminating a for loop in Python
Sometimes it’s necessary to skip individual iterations of a loop. Like other languages, Python has a continue statement for jumping to the next iteration.
The continue statement can be used much like early return. For example, we might want to skip past an iteration after it becomes clear that the data set doesn’t have the characteristic we’re looking for:
def process_data(data):
for data_set in data:
data_set.validate()
# early continue after cheap check fails
if not data_set.quality_ok():
continue
# expensive operation guarded by early continue
data_set.process()
Or, as another example, we output a text and skip every other letter:
text = 'Skipping every second letter'
for index, letter in enumerate(text):
if index % 2 != 0 and letter != ' ':
continue
print(letter)
In addition to the continue statement, there’s also the break statement, which terminates the loop. The role of break in loops is similar to that of return in functions.
The break statement is often used in search algorithms. If the element being searched for was found, it’s not necessary to execute further iterations. In the example below, we check the list for the presence of a single true value and terminate it once we’ve found one:
bool_list = [False, False, True, False]
for index, boolean in enumerate(bool_list):
if boolean:
print(f"Value at position {index + 1} is True")
print(f"Aborting inspection of remaining {len(bool_list) - index - 1} item(s)")
break
For loops in Python can contain optional 'else' bodies. The body of else will be executed when the loop is terminated, without the break statement having been executed:
def find_element(target, collection):
for element in collection:
if element == target:
print("Found what you're looking for")
break
else:
print("Didn't find what you were looking for")
# test
find_element('a', 'Python')
find_element('o', 'Python')
For loops in Python often appear as parts of functions. In this case, you would use a return statement rather than a break statement. Take a look at this alternate version of our search algorithm without break and else:
def find_element(target, collection):
for element in collection:
if element == target:
print("Found what you're looking for")
# returning breaks us out of the loop
return element
# we made it here without returning
print("Didn't find what you were looking for")
return None
# test
print(find_element('a', 'Python'))
print(find_element('o', 'Python'))
What are the best practices for for loops in Python?
For loops in Python are primarily used to iterate over the elements of a sequence or collection. But there are also more direct methods for many common use cases. Below we’ll look at some best practices and anti-patterns. First, you’ll want to review these key terms:
Term | Explanation | Example |
---|---|---|
Collection | Several elements that have been brought together; a collection is an iterable | ('Walter', 'White'), [4, 2, 6, 9], 'Python' |
Iterator | Interface for iterating over collections | it = iter('Python') |
Generator | Function that uses a yield statement rather than a return statement; a generator is an iterable | range(10) |
Comprehension | Iterating expression; creates a new collection based on an iterable | [num ** 2 for num in range(10)] |
Directly iterating over the elements in a collection
One mistake often made by beginners is to use the len() function as a limit to the range() function. This creates a numerical loop variable, indexing the individual elements of the collection.
word = 'Python'
for i in range(len(word)):
print(word[i])
This anti-pattern is frowned upon, with good reason. It’s much better to use the Python for loop to iterate directly over the elements in a collection:
word = 'Python'
for letter in word:
print(letter)
Listing the elements in a collection with enumerate(), including index
Sometimes you need the index of an element in a collection. Instead of creating the index as a loop variable, use the enumerate() function. The function will return the tuple (index, element). Make sure that the index starts counting at zero.
names = ["Jim", "Jack", "John"]
for index, name in enumerate(names):
print(f"{index + 1}. {name}")
Iterating over tuples with the zip() function
Another situation that frequently arises is simultaneously iterating over elements from two collections of equal length. The Python approach to this situation uses the zip() function, which takes two collections of the same size and returns 2-tuples:
people = ('Jim', 'Jack', 'John')
ages = (42, 69, 13)
# ascertain both collections are same length
assert len(people) == len(ages)
# iterate over tuples of (person, age)
for person, age in zip(people, ages):
print(f"{person} is {age} years old")
Generating a numerical loop variable with the range() function
For loops are normally used in Python to iterate over the elements in a collection. They usually aren’t used to increment an integer. The best way to do this is to create a range object using the range() function and iterate over that:
for counter in range(10):
print(counter)
Using the in operator to test whether a collection contains an element
Finding an element from within a collection is a fundamental tool for any programmer. Normally, you would use a function that iterates over the elements and checks each element against the element being searched for. When the element is found, the iteration is terminated.
In Python there’s a special operator for this purpose, the in operator. It checks whether a collection contains the element being searched for and returns the corresponding Boolean value:
'a' in 'Python'
'y' in 'Python'
Creating a list from an iterable using the list() function
Unlike in many other programming languages, you don’t need to use a for loop in Python to bring the individual letters from a string into a list. In Python, you can use the list() function to convert an iterable into a list of elements. Let’s take a look at those two different approaches. Using the first approach, we would iterate over the letters in a word and insert them into an empty list:
word = 'Python'
letters = []
for letter in word:
letters.append(letter)
We can save ourselves all these extra steps. Using the second approach, we can create a list right away with the list() function. At the same time, we can check that the two approaches deliver the same result:
assert list(word) == letters
Let’s look at another example. We’ll create a list of numbers from zero to nine using a range object as the iterable:
list(range(10))
Like lists, sets can also be created using iterables. Below we’ll create a set that contains all the letters in a certain sentence. Then we’ll use the in operator to check that the set doesn’t contain an 'a':
alphabet = set('Python is not hyped')
assert 'a' not in alphabet
Replacing Python for loops with comprehensions
One common use for for loops in Python is modifying the elements of a collection. For example, you might want to calculate new values based on a collection or filter certain elements. The steps involved in doing this with a for loop would be as follows:
- Iterate over the collecting using a for loop.
- Process each element.
- If necessary, create a new collection from subsets of the elements.
This is a lot of work for simple modifications. Functional languages show us that it can be easier. Luckily Python has comprehensions, which can replace for loops in simple cases and perform better.
Comprehensions create modified collections based on iterables using concise and effective syntax. Let’s take a look at the general syntax of a list comprehension. We’ll write the expression in square brackets. In this example, an operation is carried out on the elements of a collection, and each element is copied into a new list:
[ operation(element) for element in collection ]
Elements can also be filtered using the if statement and a condition:
[ operation(element) for element in collection if condition(element) ]
Let’s now take a look at an example of a for loop in Python that can be replaced by a comprehension. We start with a list of numbers and want to calculate a list of their squares:
numbers = [2, 3, 5, 9, 17]
If we wanted to use a for loop, we would create an empty list and fill it with the squares as a part of the loop:
squares = []
for number in numbers:
squares.append(number ** 2)
However, this can be done much more easily using a comprehension:
squares_comp = [number ** 2 for number in numbers]
We can use the assert statement to check whether the two approaches deliver the same result:
assert squares == squares_comp
Let’s take a look at another example. Say we want to extract all the lowercase letters from a string. First we’ll create a list consisting of both lowercase and uppercase letters:
word = list("PyThoN")
If we wanted to use a for loop, we’d iterate over the letters and test each letter using the islower() function. Those that pass the test would be added to an empty list:
lowers = []
for letter in word:
if letter.islower():
lowers.append(letter)
However, there’s no need for this for loop in Python. Instead we’ll use a comprehension, which only copies the lowercase letters from the original list:
lowers_comp = [ letter for letter in word if letter.islower() ]
Finally, we’ll again use the assert statement to make sure the two approaches deliver the same result:
assert lowers == lowers_comp