lists: list comprehensions¶
List Comprehensions are a way to make a list from an iterable. It is a simple way to go over every item in the list and perform an operation usually in one line
test_making_a_list_from_an_iterable¶
red: make it fail¶
I add a file called test_list_comprehensions.py
to the tests
folder with the following code
import unittest
class TestListComprehensions(unittest.TestCase):
def test_making_a_list_from_an_iterable(self):
a_list = []
self.assertEqual(a_list, [])
container = range(10)
for item in container:
a_list.append(item)
self.assertEqual(a_list, [])
a_list = []
makes an empty list calleda_list
self.assertEqual(a_list, [])
confirms thata_list
is empty since it is equal to[]
container = range(10)
makes an iterable of numbers from 0 to 9 with the range constructor and calls itcontainer
range makes an iterable of numbers from 0 to the given number minus 1
for item in container:
uses a for statement to make a loop that goes over every item ofcontainer
a_list.append(item)
adds the item fromcontainer
toa_list
on each cycle of the loop, using theappend
method, see lists for more detailsthe second
self.assertEqual(a_list, [])
checks to see ifa_list
is still empty after the operation
the terminal shows AssertionError because a_list
is no longer empty, it has 10 items after the loop runs
E AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] != []
green: make it pass¶
I make the values in the test match the result
def test_making_a_list_from_an_iterable(self):
a_list = []
self.assertEqual(a_list, [])
container = range(10)
for item in container:
a_list.append(item)
self.assertEqual(
a_list,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
)
and the test passes
refactor: make it better¶
I add another test to check what happens when I use the list constructor on
container
self.assertEqual(list(container), [])
the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] != []
I make the empty list
[]
toa_list
self.assertEqual(list(container), a_list)
and the test passes because calling
list
on aniterable
makes a listI add another test
self.assertEqual( list_comprehensions.make_a_list(container), a_list )
the terminal shows NameError
NameError: name 'list_comprehensions' is not defined
I add it to the list of Exceptions encountered
# Exceptions Encountered # AssertionError # NameError
then add an import statement for
list_comprehensions
at the beginning oftest_list_comprehensions.py
to define the name in the testsimport list_comprehensions import unittest
the terminal shows ModuleNotFoundError
ModuleNotFoundError: No module named 'list_comprehensions'
I add it to the list of Exceptions encountered
# Exceptions Encountered # AssertionError # NameError # ModuleNotFoundError
then make a file called
list_comprehensions.py
in the project folder and the terminal shows AttributeErrorAttributeError: module 'list_comprehensions' has no attribute 'make_a_list'
I add the error to the list of Exceptions encountered
# Exceptions Encountered # AssertionError # NameError # ModuleNotFoundError # AttributeError
then add a function definition to
list_comprehensions.py
def make_a_list(): return None
and the terminal shows TypeError
TypeError: make_a_list() takes 0 positional arguments but 1 was given
I add it to the list of Exceptions encountered
# Exceptions Encountered # AssertionError # NameError # ModuleNotFoundError # AttributeError # TypeError
then change the signature of the function to take in an argument
def make_a_list(argument): return None
and the terminal shows AssertionError
AssertionError: None != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
When I make the function return the result of calling the list constructor with
argument
as inputdef make_a_list(argument): return list(argument)
the test passes
I make the name of the function’s input from
argument
toiterable
to make it more explicitdef make_a_list(iterable): return list(iterable)
From the tests I see that I can make a list from any iterable by using the list constructor
test_making_a_list_w_a_for_loop¶
red: make it fail¶
I add a test for making a list with a for loop
def test_making_a_list_w_a_for_loop(self):
a_list = []
self.assertEqual(a_list, [])
container = range(10)
for item in container:
a_list.append(item)
self.assertEqual(a_list, [])
self.assertEqual(
list_comprehensions.for_loop(container),
a_list
)
the terminal shows AssertionError for the values of a_list
because it is no longer empty after I loop through container
then add items
AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] != []
green: make it pass¶
I make the values of the test match the result
def test_making_a_list_w_a_for_loop(self): a_list = [] self.assertEqual(a_list, []) container = range(10) for item in container: a_list.append(item) self.assertEqual( a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual( list_comprehensions.for_loop(container), a_list )
the terminal shows AttributeError since
list_comprehensions.py
does not have a definition forfor_loop
AttributeError: module 'list_comprehensions' has no attribute 'for_loop'
I add a function definition called
for_loop
tolist_comprehensions.py
def for_loop(): return None
and the terminal shows TypeError because the function signature does not match the call in the test
TypeError: for_loop() takes 0 positional arguments but 1 was given
I make the signature of the function to take input
def for_loop(argument): return None
and the terminal shows AssertionError
AssertionError: None != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I make the behavior of the function to use a
for
loopdef for_loop(argument): result = [] for item in argument: result.append(item) return result
result = []
makes an empty list calledresult
for item in argument:
makes a loop over the items ofargument
which is an iterable passed into the functionresult.append(item)
adds each item fromargument
to the list calledresult
return result
returnsresult
after the loop completes
the terminal shows all tests are passing
I change the input name from
argument
toiterable
to be more explicitdef for_loop(iterable): result = [] for item in iterable: result.append(item) return result
all tests are still passing
From the tests I see that I can make a list from any iterable by using
test_making_lists_w_list_comprehensions¶
red: make it fail¶
I add a failing test to TestListComprehensions
def test_making_lists_w_list_comprehensions(self):
a_list = []
self.assertEqual(a_list, [])
container = range(10)
for item in container:
a_list.append(item)
self.assertEqual(a_list, [])
self.assertEqual([], a_list)
self.assertEqual(
list_comprehensions.list_comprehension(container),
a_list
)
the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] != []
green: make it pass¶
I make the values in the test match the terminal
def test_making_lists_w_list_comprehensions(self): a_list = [] self.assertEqual(a_list, []) container = range(10) for item in container: a_list.append(item) self.assertEqual( a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual([], a_list) self.assertEqual( list_comprehensions.list_comprehension(container), a_list )
and the terminal shows another AssertionError for the next line
AssertionError: Lists differ: [] != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
this time I add a list comprehension to show how it is written
def test_making_lists_w_list_comprehensions(self): a_list = [] self.assertEqual(a_list, []) container = range(10) for item in container: a_list.append(item) self.assertEqual( a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual( [item for item in container], a_list ) self.assertEqual( list_comprehensions.list_comprehension(container), a_list )
the terminal now shows AttributeError for the last line
AttributeError: module 'list_comprehensions' has no attribute 'list_comprehension'
I add a function that uses a list comprehension to
list_comprehensions.py
def list_comprehension(argument): return [item for item in argument]
and all tests pass
I change
argument
toiterable
to be more explicitdef list_comprehension(iterable): return [item for item in iterable]
I made 2 functions, one that uses a for loop and another that uses a list comprehension to do the same thing. The difference between
a_list = [] for item in container: a_list.append()
and
[item for item in container]
Is that in the first case I have to
make a list
loop through the iterable
add the items I want from the iterable to the list
With the list comprehension I can get the same result with less words, lines and steps
refactor: make it better¶
There is more I can do with list comprehensions, I can add conditions to the operations performed
I add a failing test to
TestListComprehensions
def test_list_comprehensions_w_conditions_i(self): even_numbers = [] self.assertEqual(even_numbers, []) container = range(10) for item in container: if item % 2 == 0: even_numbers.append(item) self.assertEqual(even_numbers, []) self.assertEqual( [], even_numbers ) self.assertEqual( list_comprehensions.get_even_numbers(container), even_numbers )
the terminal shows AssertionError
AssertionError: Lists differ: [0, 2, 4, 6, 8] != []
if item % 2 == 0:
checks if the item incontainer
leaves a remainder of0
when divided by2
%
is a modulo operator which divides the number on the left by the number on the right and gives a remaindereven_numbers.append(item)
addsitem
toeven_numbers
ifitem
divided by2
leaves a remainder of0
I add the values of the result to the test to make it pass
def test_list_comprehensions_w_conditions_i(self): even_numbers = [] self.assertEqual(even_numbers, []) container = range(10) for item in container: if item % 2 == 0: even_numbers.append(item) self.assertEqual(even_numbers, [0, 2, 4, 6, 8]) self.assertEqual( [], even_numbers ) self.assertEqual( list_comprehensions.get_even_numbers(container), even_numbers )
and the terminal shows AssertionError for the next line
AssertionError: Lists differ: [] != [0, 2, 4, 6, 8]
I try using a list comprehension like I did in the last example
def test_list_comprehensions_w_conditions_i(self): even_numbers = [] self.assertEqual(even_numbers, []) container = range(10) for item in container: if item % 2 == 0: even_numbers.append(item) self.assertEqual(even_numbers, [0, 2, 4, 6, 8]) self.assertEqual( [item for item in container], even_numbers ) self.assertEqual( list_comprehensions.get_even_numbers(container), even_numbers )
and get AssertionError because the lists are not the same, I have too many values
AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] != [0, 2, 4, 6, 8]
When I add the
if
condition to the list comprehensionself.assertEqual( [item for item in container if item % 2 == 0], even_numbers )
the terminal shows AttributeError for the next line. Progress
AttributeError: module 'list_comprehensions' has no attribute 'get_even_numbers'
I add a function definition to
list_comprehensions.py
using the list comprehension I just wrotedef get_even_numbers(iterable): return [item for item in iterable if item % 2 == 0]
and the terminal shows passing tests, Hooray!
I want to try another list comprehension with a different condition so I add a test to
TestListComprehensions
def test_list_comprehensions_w_conditions_ii(self): odd_numbers = [] self.assertEqual(odd_numbers, []) container = range(10) for item in container: if item % 2 != 0: odd_numbers.append(item) self.assertEqual(odd_numbers, []) self.assertEqual([], odd_numbers) self.assertEqual( list_comprehensions.get_odd_numbers(container), odd_numbers )
the terminal shows AssertionError
AssertionError: Lists differ: [1, 3, 5, 7, 9] != []
when I make the values to match
def test_list_comprehensions_w_conditions_ii(self): odd_numbers = [] self.assertEqual(odd_numbers, []) container = range(10) for item in container: if item % 2 != 0: odd_numbers.append(item) self.assertEqual(odd_numbers, [1, 3, 5, 7, 9]) self.assertEqual([], odd_numbers) self.assertEqual( list_comprehensions.get_odd_numbers(container), odd_numbers )
the terminal shows AssertionError for the next test
AssertionError: Lists differ: [] != [1, 3, 5, 7, 9]
I make the value on the left with a list comprehension that uses the same condition I used to make even numbers
self.assertEqual( [item for item in container if item % 2 == 0], odd_numbers )
and the terminal shows AssertionError
AssertionError: Lists differ: [0, 2, 4, 6, 8] != [1, 3, 5, 7, 9]
When I make the logic in the condition so it uses not equal to
0
insteadself.assertEqual( [item for item in container if item % 2 != 0], odd_numbers )
the terminal shows AttributeError for the next line
AttributeError: module 'list_comprehensions' has no attribute 'get_odd_numbers'
I define a function that returns a list comprehension in
list_comprehensions.py
def get_odd_numbers(iterable): return [item for item in iterable if item % 2 != 0]
and the terminal shows all tests passed
I see from the tests that I can make a list from any iterable by using
If you typed along you now know a couple of ways to loop through
iterables
and have your program make decisions by usingconditions
.You also know how to do it with less words using list comprehensions. Your magic powers are growing.
I have written the same thing multiple times in these tests and since the programming gods told me to not repeat myself, It is time to remove the repetition in the code. In each test I make an empty list, verify it is empty and then perform operations on it. Since that part is the same for every test I can add it to the unittest.TestCase.setUp method which is called before a test method. Anything I place in this method will run before the tests, I place my empty list creation and verification in here
def setUp(self): self.a_list = [] self.assertEqual(self.a_list, [])
I make a reference to
self.a_list
intest_making_a_list_from_an_iterable
def test_making_a_list_from_an_iterable(self): a_list = [] self.assertEqual(a_list, []) container = range(10) for item in container: self.a_list.append(item) self.assertEqual( self.a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual(list(container), self.a_list) self.assertEqual( list_comprehensions.make_a_list(container), self.a_list )
the terminal still shows passing tests
I remove the creation of the empty list and verification from
test_making_a_list_from_an_iterable
def test_making_a_list_from_an_iterable(self): container = range(10) for item in container: self.a_list.append(item) self.assertEqual( self.a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual(list(container), self.a_list) self.assertEqual( list_comprehensions.make_a_list(container), self.a_list )
the terminal still shows passing tests
I make the same change in the other tests
def test_making_a_list_w_a_for_loop(self): container = range(10) for item in container: self.a_list.append(item) self.assertEqual( self.a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual( list_comprehensions.for_loop(container), self.a_list ) def test_making_lists_w_list_comprehensions(self): container = range(10) for item in container: self.a_list.append(item) self.assertEqual( self.a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual( [item for item in container], self.a_list ) self.assertEqual( list_comprehensions.list_comprehension(container), self.a_list ) def test_list_comprehensions_w_conditions_i(self): container = range(10) for item in container: if item % 2 == 0: self.a_list.append(item) self.assertEqual(self.a_list, [0, 2, 4, 6, 8]) self.assertEqual( [item for item in container if item % 2 == 0], self.a_list ) self.assertEqual( list_comprehensions.get_even_numbers(container), self.a_list ) def test_list_comprehensions_w_conditions_ii(self): container = range(10) for item in container: if item % 2 != 0: self.a_list.append(item) self.assertEqual(self.a_list, [1, 3, 5, 7, 9]) self.assertEqual( [item for item in container if item % 2 != 0], self.a_list ) self.assertEqual( list_comprehensions.get_odd_numbers(container), self.a_list )
the terminal still shows passing test
In each test I make a range object named
container
, I can add this to thesetUp
method and reference it in the testsdef setUp(self): self.a_list = [] self.assertEqual(self.a_list, []) self.container = range(10) def test_making_a_list_from_an_iterable(self): for item in self.container: self.a_list.append(item) self.assertEqual( self.a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual(list(self.container), self.a_list) self.assertEqual( list_comprehensions.make_a_list(self.container), self.a_list ) def test_making_a_list_w_a_for_loop(self): for item in self.container: self.a_list.append(item) self.assertEqual( self.a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual( list_comprehensions.for_loop(self.container), self.a_list ) def test_making_lists_w_list_comprehensions(self): for item in self.container: self.a_list.append(item) self.assertEqual( self.a_list, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ) self.assertEqual( [item for item in self.container], self.a_list ) self.assertEqual( list_comprehensions.list_comprehension(self.container), self.a_list ) def test_list_comprehensions_w_conditions_i(self): for item in self.container: if item % 2 == 0: self.a_list.append(item) self.assertEqual(self.a_list, [0, 2, 4, 6, 8]) self.assertEqual( [item for item in self.container if item % 2 == 0], self.a_list ) self.assertEqual( list_comprehensions.get_even_numbers(self.container), self.a_list ) def test_list_comprehensions_w_conditions_ii(self): for item in self.container: if item % 2 != 0: self.a_list.append(item) self.assertEqual(self.a_list, [1, 3, 5, 7, 9]) self.assertEqual( [item for item in self.container if item % 2 != 0], self.a_list ) self.assertEqual( list_comprehensions.get_odd_numbers(self.container), self.a_list )
the terminal shows all tests are still passing
review¶
I also ran into the following Exceptions
Would you like to test dictionaries?