3.1. Function Unpack¶

3.1.1. Recap¶

a = 1
a, b = 1, 2
a, b, c = 1, 2, 3

a, b, c = (1, 2, 3)
a, b, c = [1, 2, 3]
a, b, c = {1, 2, 3}

(a, b, c) = (1, 2, 3)
(a, b, c) = [1, 2, 3]

[a, b, c] = [1, 2, 3]
[a, b, c] = (1, 2, 3)


3.1.2. Rationale¶

Figure 3.13. Unpacking and Arbitrary Number of Parameters and Arguments

3.1.3. Errors¶

Listing 3.110. Note, that set is unordered collection
{a, b, c} = {1, 2, 3}
# Traceback (most recent call last):
#     ...
# SyntaxError: can't assign to literal

Listing 3.111. Too many values to unpack
a, b, c = [1, 2, 3, 4]
# Traceback (most recent call last):
#     ...
# ValueError: too many values to unpack (expected 3)

Listing 3.112. Not enough values to unpack
a, b, c, d = [1, 2, 3]
# Traceback (most recent call last):
#     ...
# ValueError: not enough values to unpack (expected 4, got 3)


3.1.4. Arbitrary Number of Arguments¶

Listing 3.113. Unpacking values at the right side
a, b, *c = [1, 2, 3, 4]

a               # 1
b               # 2
c               # [3, 4]

Listing 3.114. Unpacking values at the left side
*a, b, c = [1, 2, 3, 4]

a               # [1, 2]
b               # 3
c               # 4

Listing 3.115. Unpacking values from both sides at once
a, *b, c = [1, 2, 3, 4]

a               # 1
b               # [2, 3]
c               # 4

Listing 3.116. Unpacking from variable length
a, *b, c = [1, 2]

a               # 1
b               # []
c               # 2

Listing 3.117. Cannot unpack from both sides at once
*a, b, *c = [1, 2, 3, 4]
# Traceback (most recent call last):
#     ...
# SyntaxError: two starred expressions in assignment

Listing 3.118. Unpacking requires values for required arguments
a, *b, c = [1]
# Traceback (most recent call last):
#     ...
# ValueError: not enough values to unpack (expected at least 2, got 1)


3.1.5. Nested¶

a, (b, c) = [1, (2, 3)]

a               # 1
b               # 2
c               # 3


3.1.6. Convention¶

first, *middle, last = [1, 2, 3, 4]

first           # 1
middle          # [2, 3]
last            # 4

first, second, *others = [1, 2, 3, 4]

first               # 1
second              # 2
others              # [3, 4]


3.1.7. Skipping Values¶

• _ is regular variable name, not a special Python syntax

• _ by convention is used for data we don't want to access in future

_ = 'Jan Twardowski'

print(_)
# Jan Twardowski

line = 'Jan,Twardowski,44'

firstname, lastname, _ = line.split(',')

print(firstname)        # Jan
print(lastname)         # Twardowski

a, _, c = 1, 2, 3

print(a)                # 1
print(c)                # 3

_, b, _ = 1, 2, 3

print(b)                # 2

line = '4.9,3.1,1.5,0.1,setosa'

*_, label = line.split(',')

label                   # setosa

line = 'twardowski:x:1001:1001:Jan Twardowski:/home/twardowski:/bin/bash'

username, _, _, _, fullname, *_ = line.split(':')

fullname               # Jan Twardowski

line = 'twardowski:x:1001:1001:Jan Twardowski:/home/twardowski:/bin/bash'

username, *_, home, _ = line.split(':')

home                    # /home/twardowski

_, (interesting, _) = [1, (2, 3)]

interesting             # 2


3.1.8. Examples¶

import sys

sys.version_info
# sys.version_info(major=3, minor=9, micro=0, releaselevel='final', serial=0)

major, minor, *_ = sys.version_info
print(major, minor)
# 3.9

*features, label = (5.8, 2.7, 5.1, 1.9, 'virginica')

features                # [5.8, 2.7, 5.1, 1.9]
label                   # 'virginica'

*features, label = (5.8, 2.7, 5.1, 1.9, 'virginica')
avg = sum(features) / len(features)

print(label, avg)
# virginica 3.875

line = 'ares3,watney,lewis,vogel,johanssen'
mission, *crew = line.split(',')

mission                 # ares3
crew                    # ['watney', 'lewis', 'vogel', 'johanssen']

def parse(line):
mission, *crew = line.split(',')
crew = ' and '.join(name.title() for name in crew)
print(mission.upper(), crew)

parse('ares3,watney,lewis,vogel,johanssen')
# ARES3 Watney and Lewis and Vogel and Johanssen

parse('apollo18,twardowski,ivanovic')
# APOLLO18 Twardowski and Ivanovic

first, second, *others = range(10)

first                   # 0
second                  # 1
others                  # [2, 3, 4, 5, 6, 7, 8, 9]


3.1.9. Using in a Loop¶

*features, label = (5.8, 2.7, 5.1, 1.9, 'virginica')

features                # [5.8, 2.7, 5.1, 1.9]
label                   # 'virginica'

DATA = [
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
]

for *features, label in DATA:
avg = sum(features) / len(features)
print(label, avg)

# virginica 3.875
# setosa 2.55
# versicolor 3.475

DATA = [
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
]

for *_, label in DATA:
print(label)

# virginica
# setosa
# versicolor


3.1.10. Assignments¶

3.1.10.1. Function Unpack Flat¶

• Assignment name: Function Unpack Flat

• Last update: 2020-10-12

• Complexity level: easy

• Lines of code to write: 1 lines

• Estimated time of completion: 3 min

English
1. Use data from "Input" section (see below)

2. Using str.split() split input data by white space

3. Separate ip address and host names

4. Use asterisk * notation

5. Compare result with "Output" section (see below)

Polish
1. Użyj danych z sekcji "Input" (patrz poniżej)

2. Używając str.split() podziel dane wejściowe po białych znakach

3. Odseparuj adres ip i nazw hostów

4. Skorzystaj z notacji z gwiazdką *

5. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
DATA = '10.13.37.1      nasa.gov esa.int roscosmos.ru'

Output
>>> assert type(ip) is str
>>> assert type(hosts) is list
>>> assert all(type(host) is str for host in hosts)

>>> ip
'10.13.37.1'
>>> hosts
['nasa.gov', 'esa.int', 'roscosmos.ru']

Hints
• help(str.split)

3.1.10.2. Function Unpack Nested¶

• Assignment name: Function Unpack Nested

• Last update: 2020-10-12

• Complexity level: easy

• Lines of code to write: 1 lines

• Estimated time of completion: 3 min

English
1. Use data from "Input" section (see below)

3. Use asterisk * notation

4. Compare result with "Output" section (see below)

Polish
1. Użyj danych z sekcji "Input" (patrz poniżej)

2. Odseparuj nagłówek od danych

3. Skorzystaj z konstrukcji z gwiazdką *

4. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
DATA = [
('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
(6.3, 2.9, 5.6, 1.8, 'virginica'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(4.7, 3.2, 1.3, 0.2, 'setosa'),
]

Output
>>> assert type(header) is tuple
>>> assert all(type(x) is str for x in header)
>>> assert type(data) is list
>>> assert all(type(row) is tuple for row in data)

('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species')

>>> data  # doctest: +NORMALIZE_WHITESPACE
[(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
(6.3, 2.9, 5.6, 1.8, 'virginica'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(4.7, 3.2, 1.3, 0.2, 'setosa')]


3.1.10.3. Function Unpack Loop¶

• Assignment name: Function Unpack Loop

• Last update: 2020-10-12

• Complexity level: easy

• Lines of code to write: 4 lines

• Estimated time of completion: 5 min

English
1. Use data from "Input" section (see below)

2. Iterate over data splitting *features from label

3. Define result: list[str] with species names ending with "ca" or "osa"

4. Compare result with "Output" section (see below)

Polish
1. Użyj danych z sekcji "Input" (patrz poniżej)

2. Iteruj po danych rozdzielając *features od label

3. Zdefiniuj result: list[str] z nazwami gatunków kończącymi się na "ca" lub "osa"

4. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
DATA = [
('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
(6.3, 2.9, 5.6, 1.8, 'virginica'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(4.7, 3.2, 1.3, 0.2, 'setosa'),
]

Output
>>> assert type(result) is list
>>> assert all(type(x) is str for x in result)

>>> result
['virginica', 'setosa', 'virginica', 'setosa']

Hints
• str.endswith()