from IPython.core.display import HTML

HTML(open("custom.html", "r").read())
Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Copyright (C) 2014-2023 Scientific IT Services of ETH Zurich,
Contributing Authors: Uwe Schmitt, Mikolaj Rybniski

9. tuple unpacking, enumerate, list/dict comprehensions¶

Tuple unpacking allows taking values from a tuple without index access, so you need less code and it is often more readable. Instead of writing

tp = (1, 2, 3)
a = tp[0]
b = tp[1]
c = tp[2]
print(a + b + c)
6

... you can write:

a, b, c = tp
print(a + b + c)
6

You can omit parentheses for declaring a tuple:

a, b, c = 1, 2, 3
print(a + b + c)
6

So: multiple return values of function is nothing else than returning tuples followed by tuple unpacking.¶

Good style: use _ for not needed values if you do tuple unpacking. So if you only want to unpack a use the following style to avoid declaration of variables you will not use later on:

tp = (1, 2, 3)
a, _, _ = tp

Tuple unpacking is handy for exchanging values, you need no temporary variables:

print(a, b)
a, b = b, a
print(a, b)
1 2
2 1

If this was to fast, here is a more detailed implementation:

print(a, b)
tp = (b, a)  # creates tuple
a, b = tp  # unpacks the values and overwrites variables "a" and "b" with new values !
print(a, b)
2 1
1 2

Recent Python 3 versions also support a more flexible variant for unpacking. The variable decorated with a * captures remaining tuple elements:

a, *b, c = (1, 2, 3, 4, 5)
print(a, b, c)
1 [2, 3, 4] 5
first, second, *ignore = range(7)
print(first, second, ignore)
0 1 [2, 3, 4, 5, 6]
*ignore, last = range(8)
print(last)
7

Using zip¶

To iterate over two (or more) lists at the same time use zip + tuple unpacking after for:

numbers = [1, 2, 4, 8]
words = ["one", "two", "four", "eight"]

for number, word in zip(numbers, words):
    print(number, word)
1 one
2 two
4 four
8 eight

zip also returns an iterator which gives you a tuple in every iteration. Tuple unpacking with for now does tuple unpacking for every tuple created by the zip iterator.

print(list(zip(numbers, words)))
[(1, 'one'), (2, 'two'), (4, 'four'), (8, 'eight')]

If the iterables passed to zip have different lengths, the shortest one determines the result:

print(list(zip("abcde", "012", "XY")))
[('a', '0', 'X'), ('b', '1', 'Y')]

Iterating using enumerate¶

If you want to iterate over a list and you want to count at the same time enumerate is handy:

for i, word in enumerate(words):
    print(i, word)
0 one
1 two
2 four
3 eight

zip and enumerate work on other iterables (a file handles) as well:

with open("say_hi.txt", "r") as fh:
    for i, line in enumerate(fh):
        print("line", i, "is: ", line.rstrip())
line 0 is:  hi
line 1 is:  ho

List comprehensions¶

List comprehensions allow creation and transformation of lists in a comprehensive and readable way. For example the following two lines ...

squares = [i * i for i in range(6)]
print(squares)
[0, 1, 4, 9, 16, 25]

... are equivalent to

squares = []
for i in range(6):
    squares.append(i * i)
print(squares)
[0, 1, 4, 9, 16, 25]

But you can filter too:

squares_of_odds = [i * i for i in range(6) if i % 2 == 1]
print(squares_of_odds)
[1, 9, 25]
squares_of_odds = []
for i in range(6):
    if i % 2 == 1:
        squares_of_odds.append(i * i)
print(squares_of_odds)
[1, 9, 25]

We used range only for demonstration, you take any other iterable instead:

words = ["hi", "this", "is", "list", "comprehension"]
print([w.upper() for w in words if len(w) % 2 == 0])
['HI', 'THIS', 'IS', 'LIST']

Dictionary comprehensions¶

Recent Python 3 versions also support dictionary comprehensions like:

d = {i: i ** 2 for i in range(10) if i % 2 == 0}
print(d)
{0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

sorting¶

If you want to sort a list of strings not by their alphabetic order but by their length you can provide a key parameter which is a function which indicates the ordering:

names = ["python", "programming", "I", "like"]
print(sorted(names))
['I', 'like', 'programming', 'python']

When you sort strings upper/lower case matters:

data = ["a", "A", "aB", "Ab", "abc", "ABc"]
print(sorted(data))
['A', 'ABc', 'Ab', 'a', 'aB', 'abc']

To order by length you can specify a different sort key:

print(sorted(names, key=len))
# sort by len(x) value of each x in names
['I', 'like', 'python', 'programming']

Exercises¶

  1. Write a list comprehension to transform the list [2, 3, 5, 7, 11] to a new list such that the result contains the doubled value of elements from list being smaller than 7.

  2. Sort a list of strings ignoring their case, e.g. sorted(["ab", "AC"], key=...) should return ["ab", "AC"].

Hint: you need to define own key function.

Optional exercise*¶

  1. Can you write a list comprehension which computes a 10 x 10 multiplication table as a nested list of lists, like [[1, 2, ... 10], [2, 4, ....], ...]?