Populate a list with a for loop :

nums = [12, 8, 21, 3, 16]
new_nums = []

for num in nums:
    new_nums.append(num + 1)

print(new_nums)

->
[13, 9, 22, 4, 17]

We can do that in only one line with a list comprehension !

nums = [12, 8, 21, 3, 16]

new_nums = [num + 1 for num in nums]

print(new_nums)

->
[13, 9, 22, 4, 17]

Now a list comprehension with range() :

result = [num for num in range(11)]
print(result)

->
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

List comprehensions :

  • Collapse for loops for building lists into a single line

Components :

  • iterable
  • iterator variable (represent members of iterable)
  • Output expression

Nested loops :

pairs_1 = []
for num1 in range(0, 2):
    for num2 in range(6, 8):
        pairs_1.append(num1, num2)
print(pairs_1)

->
[(0, 6), (0, 7), (1, 6), (1, 7)]

How to do it now with a list comprehension

pairs_2 = [(num1, num2) for num1 in range(0, 2) for num2 in range(6,8)]
print(pairs_2)


->
[(0, 6), (0, 7), (1, 6), (1, 7)]

The tradeoff is readibility as it is a bit difficult to understand the line.

Some examples from the exercises :

# Create list comprehension: squares
squares = [num**2 for num in range(10)]

# Create a 5 x 5 matrix using a list of lists: matrix
matrix = [[col for col in range(5)] for row in range(5) ]

# Print the matrix
for row in matrix:
    print(row)
    
->
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]

Advanced comprehensions

Conditionals in comprehensions :

[num ** 2 for num in range(10) if num % 2 == 0]

->
[0, 4, 16, 36, 64]

Conditionals on the output expression

[num ** 2 if num % 2 == 0 else 0 for num in range(10)]

->
[0, 0, 4, 0, 16, 0, 36, 0, 64, 0]

Dict comprehensions

Use curly braces {} instead of brackets []

You put the key first then : and then the value you want to put

pos_neg = {num: -num for num in range(9)}
print(pos_neg)

{0: 0, 1: -1, 2: -2, 3: -3, 4: -4, 5: -5, 6: -6, 7: -7, 8: -8}

From the exercises

# Create a list of strings: fellowship
fellowship = ['frodo', 'samwise', 'merry', 'aragorn', 'legolas', 'boromir', 'gimli']

# Create list comprehension: new_fellowship
new_fellowship = [member for member in fellowship if len(member)>=7]

# Print the new list
print(new_fellowship)



# Create a list of strings: fellowship
fellowship = ['frodo', 'samwise', 'merry', 'aragorn', 'legolas', 'boromir', 'gimli']

# Create dict comprehension: new_fellowship
new_fellowship = {member:len(member) for member in fellowship} 

# Print the new dictionary
print(new_fellowship)

Generator expressions

To use generators, you basically do like a list comprehension but you use ( ) instead of brackets [ ]

(2 * num for num in range(10))

-><generator object <genexpr> at 0x1046bf888>

List comprehensions vs generators

  • List comprehensions – returns a list
  • Generators – returns a generator object
  • Both can be iterated over

Printing values from generators

result = (num for num in range(6))
for num in result:
    print(num)

->
0
1
2
3
4
5

result = (num for num in range(6))
print(list(result))

->
[0, 1, 2, 3, 4, 5]

it’s a bit like when we talked about iter( ). we can use a generator to stock a very big number :

[num for num in range(10**10000)]

-> Your session has been disconnected

(num for num in range(10**100000)]
<generator object..

Conditions in generator expressions

even_nums = (num for num in range(10) if num % 2 == 0)
print(list(even_nums))

->
[0, 2, 4, 6, 8]

Generator functions

  • Produces generator objects when called
  • Defined like a regular function – def
  • Yields a sequence of values instead of returning a single value
  • Generates a value with yield keyword

Build a generator function :

def num_sequence(n):
"""Generate values from 0 to n."""
    i = 0
    while i < n:
        yield i
        i += 1

Use a generator function :

result = num_sequence(5)
print(type(result))

->
<class 'generator'>
    
for item in result:
    print(item)

0
1
2
3
4

From exercises : we can loop on a list to create a generator

# Create a list of strings: lannister
lannister = ['cersei', 'jaime', 'tywin', 'tyrion', 'joffrey']

# Create a generator object: lengths
lengths = (len(person) for person in lannister)

# Iterate over and print the values in lengths
for value in lengths:
    print(value)

And now with a function with a yield :

# Create a list of strings
lannister = ['cersei', 'jaime', 'tywin', 'tyrion', 'joffrey']

# Define generator function get_lengths
def get_lengths(input_list):
    """Generator function that yields the
    length of the strings in input_list."""

    # Yield the length of a string
    for person in input_list:
        yield len(person)

# Print the values generated by get_lengths()
for value in get_lengths(lannister):
    print(value)

RECAP

Basic

[output expression for iterator variable in iterable]

Advanced

[output expression + conditional on output for iterator 
variable in iterable + conditional on iterable]