List Comprehensions¶
Loops Review¶
If we have a list:
>>> names = ['Nellie', 'Ronald', 'Judith', 'Lavonda']
How could we print out each of these names?
>>> for i in range(len(names)):
... print(names[i])
...
Don’t do this. There’s no need to get the indexes for names
. Python doesn’t have for loops the same way C does… we have for-each loops which call for-in loops or just for loops.
>>> for name in names:
... print(name)
...
How did Python know that name
was the singular version of names
?
Because we told it! We can name that variable anything we want:
>>> for x in names:
... print(x)
...
What if we really needed the indexes for looping… say we want to loop over two lists at once:
>>> colors = ["red", "green", "blue", "purple"]
>>> ratios = [0.2, 0.3, 0.1, 0.4]
>>> for i, color in enumerate(colors):
... print(f"{ratios[i] * 100}% {color}")
We could use enumerate
to get the index.
Do we really need the index though? We’re only using it to look up the corresponding item in our second.
Is there a better way to do this?
>>> colors = ["red", "green", "blue", "purple"]
>>> ratios = [0.2, 0.3, 0.1, 0.4]
>>> for color, ratio in zip(colors, ratios):
... print(f"{ratio * 100}% {color}")
Yes: use zip
! The zip
function is meant for looping over multiple things at the same time.
How do you loop over a dictionary in Python?
>>> animals = {'birds': 3, 'cats': 2, 'dogs': 1}
>>> for animal in animals:
... print(f"I have {animals[animal]} {animal}")
...
Is there a better way?
>>> animals = {'birds': 3, 'cats': 2, 'dogs': 1}
>>> for item in animals.items():
... print(f"I have {item[1]} {item[0]}")
...
Is that good enough?
>>> animals = {'birds': 3, 'cats': 2, 'dogs': 1}
>>> for animal, count in animals.items():
... print(f"I have {count} {animal}")
...
That’s called tuple unpacking, iterable unpacking, or multiple assignment and it’s one of the overlooked features in Python.
Basic Comprehensions¶
Let’s say we have a list of numbers and we want to double each number. With what we have learned so far, our code would look something like this:
>>> my_favorite_numbers = [1, 1, 2, 3, 5, 8, 13]
>>> doubled_numbers = []
>>> for n in my_favorite_numbers:
... doubled_numbers.append(n * 2)
...
>>> doubled_numbers
[2, 2, 4, 6, 10, 16, 26]
In Python there is a shorter syntax for this. We can write the code to create our doubled_numbers
list in only one line:
>>> doubled_numbers = [n * 2 for n in my_favorite_numbers]
>>> doubled_numbers
[2, 2, 4, 6, 10, 16, 26]
This is called a list comprehension. List comprehensions provide convenient shorthand for creating lists from other lists or iterables.
We can put any expression that makes a new object inside of the first part of a comprehension.
Let’s create a list of number tuples where the second item of the tuple is the square of the first:
>>> squares = [(x, x ** 2) for x in range(1, 11)]
>>> squares
[(1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49), (8, 64), (9, 81), (10, 100)]
Let’s call a string methods within a comprehension:
>>> caps = [color.upper() for color in colors]
>>> caps
['RED', 'GREEN', 'BLUE', 'YELLOW']
We could also use comprehensions to get specific digits in a string:
number = 4321
digits = [int(d) for d in str(number)]
print(digits # prints [4, 3, 2, 1])
We can nest list comprehensions to make more complicated lists. Let’s create a matrix, then create the transpose of the matrix:
>>> matrix = [[r* 3+i for i in range(1, 4)] for r in range(4)]
>>> matrix
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
Conditional Filters¶
A powerful feature of list comprehensions is the ability to use a conditional if clause to add a filter to the iterable. Say we want a list of cubes of all perfect squares up through 100:
>>> [n**3 for n in range(101) if sqrt(n).is_integer()]
[0, 1, 64, 729, 4096, 15625, 46656, 117649, 262144, 531441, 1000000]
Let’s make a list comprehension that gets all numbers from a list that are greater than zero:
>>> nums = [4, -1, 7, 9, 34, 0, -4, 3]
>>> new_nums = [n for n in nums if n > 0]
>>> new_nums
[4, 7, 9, 34, 3]
Readability¶
To make your comprehensions more readable, I recommend always breaking them over multiple lines of code.
I also recommend that you write comprehensions by writing a for
loop first and then copy-pasting your way from a loop to a comprehension.
Let’s copy-paste our way from a loop to a comprehension:
pet_counts = {'cats' : 6, 'dogs' : 4, 'hamsters' : 7, 'birds' : 3}
too_many = []
for pet, num in num_pets.items():
if num > 4:
too_many.append(pet)
Let’s copy-paste our way into a comprehension:
too_many = [
pet
for pet, num in num_pets.items()
if num > 4
]
Comprehensions can always be written over multiple lines and doing so often improves readability.
Comprehension Exercises¶
These exercises are all in the lists.py
file in the exercises
directory. Edit the file to add the functions or fix the error(s) in the existing function(s). To run the test: from the exercises
folder, type python test.py <function_name>
, like this:
$ python test.py get_vowel_names
Tip
You should use at least one list comprehension in each of these exercises!
Starting with a vowel¶
Edit the get_vowel_names
function so that it accepts a list of names and returns a new list containing all names that start with a vowel. It should work like this:
>>> from lists import get_vowel_names
>>> names = ["Alice", "Bob", "Christy", "Jules"]
>>> get_vowel_names(names)
['Alice']
>>> names = ["scott", "arthur", "jan", "elizabeth"]
>>> get_vowel_names(names)
['arthur', 'elizabeth']
Power List By Index¶
Edit the power_list
function so that it accepts a list of numbers and returns a new list that contains each number raised to the i
-th power where i
is the index of that number in the given list. For example:
>>> from lists import power_list
>>> power_list([3, 2, 5])
[1, 2, 25]
>>> numbers = [78, 700, 82, 16, 2, 3, 9.5]
>>> power_list(numbers)
[1, 700, 6724, 4096, 16, 243, 735091.890625]
Flatten a Matrix¶
Edit the flatten
function to that it will take a matrix (a list of lists) and return a flattened version of the matrix.
>>> from lists import flatten
>>> matrix = [[row * 3 + incr for incr in range(1, 4)] for row in range(4)]
>>> matrix
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
>>> flatten(matrix)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Reverse Difference¶
Edit the function reverse_difference
so that it accepts a list of numbers and returns a new copy of the list with the reverse of the list subtracted.
Example usage:
>>> from lists import reverse_difference
>>> reverse_difference([9, 8, 7, 6])
[3, 1, -1, -3]
>>> reverse_difference([1, 2, 3, 4, 5])
[-4, -2, 0, 2, 4]
>>> reverse_difference([3, 2, 1, 0])
[3, 1, -1, -3]
>>> reverse_difference([0, 0])
[0, 0]
Matrix Addition¶
Edit the function matrix_add
so that it takes two matrices (lists of lists of numbers) and returns a new matrix (list of lists of numbers) with the corresponding numbers added together.
Example usage:
>>> from lists import matrix_add
>>> m1 = [[6, 6], [3, 1]]
>>> m2 = [[1, 2], [3, 4]]
>>> matrix_add(m1, m2)
[[7, 8], [6, 5]]
>>> m1
[[6, 6], [3, 1]]
>>> m2
[[1, 2], [3, 4]]
>>> matrix_add([[5]], [[-2]])
[[3]]
>>> m1 = [[1, 2, 3], [4, 5, 6]]
>>> m2 = [[-1, -2, -3], [-4, -5, -6]]
>>> matrix_add(m1, m2)
[[0, 0, 0], [0, 0, 0]]
Transpose¶
File: Edit the transpose
function in the lists.py
file.
Test: Run python test.py transpose
in your exercises
directory.
Exercise: Make a function transpose
that accepts a list of lists and returns the transpose of the list of lists.
Example usage:
>>> from zip import transpose
>>> transpose([[1, 2], [3, 4]])
[[1, 3], [2, 4]]
>>> matrix = [['a','b','c'],['d','e','f'],['g','h','i']]
>>> transpose(matrix)
[['a', 'd', 'g'], ['b', 'e', 'h'], ['c', 'f', 'i']]
Factors¶
File: Edit the get_factors
function in the lists.py
file.
Test: Run python test.py get_factors
in your exercises
directory.
Exercise: The function get_factors
returns the factors of a given number.
Example:
>>> from lists import get_factors
>>> get_factors(2)
[1, 2]
>>> get_factors(6)
[1, 2, 3, 6]
>>> get_factors(100)
[1, 2, 4, 5, 10, 20, 25, 50, 100]
Pythagorean Triples¶
Edit the triples
function so that it takes a number and returns a list of tuples of 3 integers where each tuple is a Pythagorean triple, and the integers are all less then the input number.
A Pythagorean triple is a group of 3 integers a
, b
, and c
, such that they satisfy the formula a**2 + b**2 = c**2
>>> from lists import triples
>>> triples(15)
[(3, 4, 5), (5, 12, 13), (6, 8, 10)]
>>> triples(30)
[(3, 4, 5), (5, 12, 13), (6, 8, 10), (7, 24, 25), (8, 15, 17), (9, 12, 15), (10, 24, 26), (12, 16, 20), (15, 20, 25), (20, 21, 29)]