Song of the day: My List To You by Yoshiko Tsushima (2021).
We often use for
-loops to create lists. For instance, if we wanted a list of all the lower-case letters of the
English alphabet, we would do:
alphabet = []
for ascii_code in range(ord('a'), ord('z') + 1):
alphabet.append(chr(ascii_code))
Or perhaps for creating a list with specific requirements:
upper_limit = 101
divisible_by_5_and_7 = []
for number in range(upper_limit):
if number % 5 == 0 or number % 7 == 0:
divisible_by_5_and_7.append(number)
Or even wanting to create lists based on other lists:
names = ["Chopin", "Debussy", "Ravel", 123, None, False, "Saint-Saëns"]
uppercase_names = []
for name in names:
if type(name) == str:
uppercase_names.append(name.upper())
These are relatively common operations, and having to write multiline loops for such simple processes can clutter up and
even obfuscate one's code. To that end, some languages (like Python) have this brilliant little thing called list
comprehensions. The way I like to think of them is as "one-liner" for
-loops that create lists. The general
format for list comprehensions is as follows:
list_variable = [expression for loop_variable in iterable]
That is:
Give me a list whose components will be the result of a certain expression for every element (loop variable) in an iterable.
Let's take a look at the above three problems and convert them into list comprehensions:
# Getting a list of lowercase letters in the alphabet
alphabet = [chr(ascii_code) for ascii_code in range(ord('a'), ord('z') + 1)]
Here, the expression is chr(ascii_code)
—that is, the character equivalent to the loop variable value ascii_code
. The
iterable, of course, is range(ord('a'), ord('z') + 1)
.
If you have a certain condition to an expression being added as a member of your list, you can add it at the end of the list comprehension as such:
# Get list of numbers from 0 to 100 IFF they are divisible by both 5 and 7
limit = 101
divisible_by_5_and_7 = [number for number in range(limit) if number % 5 and number % 7]
So this expands our general form to the following:
list_variable = [expression for loop_variable in iterable if condition]
That is:
Give me a list whose components will be the result of a certain expression for every element (loop variable) in an iterable if it meets a certain condition.
Finally:
# Get a list of uppercased names if they are strings
names = ["Chopin", "Debussy", "Ravel", 123, None, False, "Saint-Saëns"]
uppercase_names = [name.upper() for name in names if type(name) == str]
List comprehensions are super convenient for things like these because they are concise and easy to read. However, as the name implies, they are generally used only to create lists. You can technically use them to execute a certain function for every element in an iterable. For instance, if all you wanted to do is print the elements of a list, you could do the following:
lst = ["This", "is", "a", "weird", "flex.\n"]
[print(element, end=" ") for element in lst]
Output:
This is a weird flex.
Which, I mean, works, but it looks kind of silly. It's kind of the equivalent of using a screwdriver to crack an egg
open—sure, you can do it, but that's not really what a screwdriver is for. Using them this way might actually end up
confusing people more than a regular for
-loop might. And that's exactly the opposite of what list comprehensions are
for.
If you're having trouble coming up with a list comprehension from scratch, try doing it with a for
-loop first, and
then see if you can fit the parts into the general [expression for loop_variable in iterable if condition]
form.
List comprehensions often help, but there's no reason to force them where you feel like they might not belong.
By the way, you can also do the equivalent of a nested for
-loop with comprehensions. Let's say we wanted the
multiplication tables from 1 to 10:
# Using a for-loop
table = []
for row_number in range(1, 11):
row = []
for col_number in range(1, 11):
row.append(row_number * col_number)
table.append(row)
# Using list comprehension
table = [[row_number * col_number for col_number in range(1, 11)] for row_number in range(1, 11)]
While this won't be on the final, I just want to make you aware that you can also do this to create dictionaries.
The general form is as follows:
new_dict = { key_expr: value_expr for loop_variable in iterable }
Let's see an example of this. If we had a list of tuples where the first element was the country, and the second was the name of its capital, we could form a dictionary where the key is the name of the country, and the value is the name of the capital:
map_info = [("Mexico", "Mexico City"), ("France", "Paris"), ("Lisboa", "Portugal"), ("Tokyo", "Japan"), ("Algeria", "Algiers")]
capitals = { country[0]: country[1] for country in map_info }
print(capitals)
Output:
{'Mexico': 'Mexico City', 'France': 'Paris', 'Lisboa': 'Portugal', 'Tokyo': 'Japan', 'Algeria': 'Algiers'}
Previous: Dictionaries Methods | Next: Introduction to Object-Oriented Programming