= "hello world"
s list(s)
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
Comprehensions are a special kind of expression in Python that perform significant work within a list
, dict
, set
literal.
It is common to have code that converts one iterable to a new iterable, often building one element at a time:
= [1, 2, 4, 8, 9, 12]
some_iterable = []
new_iterable for i in some_iterable:
if some_cond:
new_iterable.append(some_func(i))return new_iterable
Comprehensions allow us to reduce the above to a single line. Additionally, they can be much more efficient than building a list, dict, or set one element at a time. assuming we want to use the new_iterable.
This is not true if you are just trying to iterate: if you just want to do work in a for
loop, continue to use a for loop as you have been!
Create a new list from another iterable.
Remember that we can take any iterable and convert it to a list by calling list()
:
= "hello world"
s list(s)
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
The simplest list comprehension does the same thing (you should prefer list
if this is all you are doing):
= (1, 2, 3, 4, 5)
t for i in t] [i
[1, 2, 3, 4, 5]
This looks a bit like an inside-out for loop:
# basic list comprehension syntax
= [expression for var in iterable] new_list
The for var in iterable
portion declares a new temporary variable for iteration, just like a traditional for loop, but The benefit comes in when we start modifying the expression portion:
**2 for i in t] [i
[1, 4, 9, 16, 25]
for c in s] [c.upper()
['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D']
These list comprehensions map values from the original iterable to new values, the same as map
!
But they do not need to be one-to-one, we can remove some items conditionally with one more optional clause:
# full list comprehension syntax with optional if clause
= [expression for var in iterable if condition] new_list
def isvowel(c):
return c in "aeiou"
for c in s if isvowel(c)]
[c.upper() # or [c.upper() for c in s if c in "aeiou"]
['E', 'O', 'O']
This can replace filter
!
Since comprehensions are expressions, and the first clause needs to be an expression, we can nest comprehensions:
# possible to nest comprehensions, but beware readability
= ("K", "Q", "J")
faces = ("♠", "♣", "♦", "♥")
suits + suit) for face in faces for suit in suits if face != "K"] [(face
['Q♠', 'Q♣', 'Q♦', 'Q♥', 'J♠', 'J♣', 'J♦', 'J♥']
It is also possible to make set
and dict
comprehensions by using {}
.
# powers of two set
2 ** n for n in [1,1,2,2,3,3,3,4,4,4]} {
{2, 4, 8, 16}
# powers of two mapping
+2: n+1 for n in range(5) if n > 0} {n
{3: 2, 4: 3, 5: 4, 6: 5}
If the expression contains a :
(colon), result is a dictionary.
Here is a way of thinking about list comprehensions that may help you write more complex comprehensions in a natural way:
def make_powers_lc(n, inputs):
"""
Given list of inputs, return each input raised to the 1st-Nth power
e.g.
>>> make_powers_lc(4, range(10))
[[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 4, 8, 16],
[3, 9, 27, 81],
[4, 16, 64, 256],
[5, 25, 125, 625],
[6, 36, 216, 1296],
[7, 49, 343, 2401],
[8, 64, 512, 4096],
[9, 81, 729, 6561]]
"""
# 1) start with shape:
# Output will be a list of list of ints,
# so create simplest version of that
def make_powers_lc(n, inputs):
return [[1]]
4, range(10)) make_powers_lc(
[[1]]
# 2) expand outer list comprehension to have correct number of elements
# using inputs as foundation, we haven't modified the first term yet
def make_powers_lc(n, inputs):
return [[1] for x in inputs]
4, range(10)) make_powers_lc(
[[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
# 3) Now expand inner list to have correct number of elements.
def make_powers_lc(n, inputs):
return [[1 for y in range(n)] for x in inputs]
4, range(10)) make_powers_lc(
[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]
# 4) Fix initial term to do calculation
def make_powers_lc(n, inputs):
return [[x**y for y in range(n)] for x in inputs]
4, range(10)) make_powers_lc(
[[1, 0, 0, 0],
[1, 1, 1, 1],
[1, 2, 4, 8],
[1, 3, 9, 27],
[1, 4, 16, 64],
[1, 5, 25, 125],
[1, 6, 36, 216],
[1, 7, 49, 343],
[1, 8, 64, 512],
[1, 9, 81, 729]]
# 5) With correct shape, make modifications to ranges/etc. as needed
def make_powers_lc(n, inputs):
return [[x**y for y in range(1, n+1)] for x in inputs]
4, range(10)) make_powers_lc(
[[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 4, 8, 16],
[3, 9, 27, 81],
[4, 16, 64, 256],
[5, 25, 125, 625],
[6, 36, 216, 1296],
[7, 49, 343, 2401],
[8, 64, 512, 4096],
[9, 81, 729, 6561]]