2.1. Star Assignment¶
a, b, *c = 1, 2, 3, 4, 5
Used when there is arbitrary number of values to unpack
Could be used from start, middle, end
There can't be multiple star expressions in one assignment statement
_
is regular variable name, not a special Python syntax_
by convention is used for data we don't want to access in future

2.1.1. Example¶
>>> def get_user_details(username):
... return 'Mark', 'Watney', 'mwatney@nasa.gov', 'mwatney@esa.int', 'mwatney@polsa.gov.pl'
>>>
>>>
>>> firstname, lastname, *email = get_user_details('mwatney')
>>>
>>> firstname
'Mark'
>>>
>>> lastname
'Watney'
>>>
>>> email
['mwatney@nasa.gov', 'mwatney@esa.int', 'mwatney@polsa.gov.pl']
2.1.2. Arbitrary Number of Arguments¶
Unpack values at the right side:
>>> a, b, *c = [1, 2, 3, 4, 5]
>>>
>>> print(f'{a=}, {b=}, {c=}')
a=1, b=2, c=[3, 4, 5]
Unpack values at the left side:
>>> *a, b, c = [1, 2, 3, 4, 5]
>>>
>>> print(f'{a=}, {b=}, {c=}')
a=[1, 2, 3], b=4, c=5
Unpack values from both sides at once:
>>> a, *b, c = [1, 2, 3, 4, 5]
>>>
>>> print(f'{a=}, {b=}, {c=}')
a=1, b=[2, 3, 4], c=5
Unpack from variable length:
>>> a, *b, c = [1, 2]
>>>
>>> print(f'{a=}, {b=}, {c=}')
a=1, b=[], c=2
2.1.3. Errors¶
Cannot unpack from both sides at once:
>>> *a, b, *c = [1, 2, 3, 4, 5]
Traceback (most recent call last):
SyntaxError: multiple starred expressions in assignment
Unpack 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)
2.1.4. 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
>>> _ = 'Mark Watney'
>>>
>>> print(_)
Mark Watney
>>> line = 'Mark,Watney,mwatney@nasa.gov,mwatney@esa.int,mwatney@polsa.gov.pl'
>>> firstname, lastname, *_ = line.split(',')
>>>
>>> print(f'{firstname=}, {lastname=}')
firstname='Mark', lastname='Watney'
>>> line = '4.9,3.1,1.5,0.1,setosa'
>>> *_, label = line.split(',')
>>>
>>> print(f'{label=}')
label='setosa'
>>> line = 'watney:x:1000:1000:Mark Watney:/home/watney:/bin/bash'
>>> username, _, uid, *_ = line.split(':')
>>>
>>> print(f'{username=}, {uid=}')
username='watney', uid='1000'
2.1.5. For Loop Unpacking¶
>>> 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 row in DATA:
... print(f'{row=}')
...
row=(5.8, 2.7, 5.1, 1.9, 'virginica')
row=(5.1, 3.5, 1.4, 0.2, 'setosa')
row=(5.7, 2.8, 4.1, 1.3, 'versicolor')
>>> for row in DATA:
... values = row[0:4]
... species = row[-1]
... print(f'{values=}, {species=}')
...
values=(5.8, 2.7, 5.1, 1.9), species='virginica'
values=(5.1, 3.5, 1.4, 0.2), species='setosa'
values=(5.7, 2.8, 4.1, 1.3), species='versicolor'
>>> for row in DATA:
... *values, species = row
... print(f'{values=}, {species=}')
...
values=[5.8, 2.7, 5.1, 1.9], species='virginica'
values=[5.1, 3.5, 1.4, 0.2], species='setosa'
values=[5.7, 2.8, 4.1, 1.3], species='versicolor'
>>> for *values, species in DATA:
... print(f'{values=}, {species=}')
...
values=[5.8, 2.7, 5.1, 1.9], species='virginica'
values=[5.1, 3.5, 1.4, 0.2], species='setosa'
values=[5.7, 2.8, 4.1, 1.3], species='versicolor'
2.1.6. Multi Dimensional¶
>>> from pprint import pprint
>>> 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'),
... ]
>>> header = DATA[0]
>>> rows = DATA[1:]
>>>
>>> pprint(header)
('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species')
>>>
>>> pprint(rows)
[(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')]
>>> header, *rows = DATA
>>>
>>> pprint(header)
('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species')
>>>
>>> pprint(rows)
[(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')]
2.1.7. Merge¶
>>> def echo(**a, **b):
... return locals()
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument
2.1.8. Use Case - 0x01¶
>>> a, b, c = range(0, 3)
>>> a, b, c, d, e = range(0, 5)
>>> a, b, *c = range(0, 10)
2.1.9. Use Case - 0x01¶
>>> line = 'ares3,watney,lewis,vogel,johanssen'
>>> mission, *crew = line.split(',')
>>>
>>> print(f'{mission=}, {crew=}')
mission='ares3', crew=['watney', 'lewis', 'vogel', 'johanssen']
2.1.10. Use Case - 0x02¶
>>> first, *middle, last = [1, 2, 3, 4]
>>>
>>> print(f'{first=}, {middle=}, {last=}')
first=1, middle=[2, 3], last=4
>>> first, second, *others = [1, 2, 3, 4]
>>>
>>> print(f'{first=}, {second=}, {others=}')
first=1, second=2, others=[3, 4]
2.1.11. Use Case - 0x03¶
>>> first, second, *others = range(0,10)
>>>
>>> print(f'{first=}, {second=}, {others=}')
first=0, second=1, others=[2, 3, 4, 5, 6, 7, 8, 9]
>>> first, second, *_ = range(0,10)
>>>
>>> print(f'{first=}, {second=}')
first=0, second=1
2.1.12. Use Case - 0x04¶
Python Version
>>> import sys
>>>
>>>
>>> major, minor, *_ = sys.version_info
>>>
>>> print(major, minor, sep='.')
3.11
2.1.13. Use Case - 0x05¶
Iris 1D
>>> *values, species = (5.8, 2.7, 5.1, 1.9, 'virginica')
>>>
>>> print(f'{values=}, {species=}')
values=[5.8, 2.7, 5.1, 1.9], species='virginica'
2.1.14. Use Case - 0x06¶
>>> *values, species = (5.8, 2.7, 5.1, 1.9, 'virginica')
>>> avg = sum(values) / len(values)
>>>
>>> print(f'{avg=:.2f}, {species=}')
avg=3.88, species='virginica'
2.1.15. Use Case - 0x07¶
>>> line = '1969-07-21, 02:56:15, WARNING, Neil Armstrong first words on the Moon'
>>> d, t, lvl, *msg = line.split(', ')
>>>
>>> d
'1969-07-21'
>>> t
'02:56:15'
>>> lvl
'WARNING'
>>> msg
['Neil Armstrong first words on the Moon']
2.1.16. Use Case - 0x08¶
>>> line = 'watney:x:1000:1000:Mark Watney:/home/watney:/bin/bash'
>>> username, password, uid, *others = line.split(':')
>>>
>>> username
'watney'
>>> password
'x'
>>> uid
'1000'
>>> others
['1000', 'Mark Watney', '/home/watney', '/bin/bash']
2.1.17. Use Case - 0x09¶
>>> line = 'watney:x:1000:1000:Mark Watney:/home/watney:/bin/bash'
>>> username, _, uid, *_ = line.split(':')
>>>
>>> username
'watney'
>>> uid
'1000'
2.1.18. Use Case - 0x0A¶
>>> line = '4.9,3.1,1.5,0.1,setosa'
>>> *values, species = line.split(',')
>>> values
['4.9', '3.1', '1.5', '0.1']
>>> species
'setosa'
2.1.19. Use Case - 0x0B¶
>>> data = (5.8, 2.7, 5.1, 1.9, 'virginica')
>>> *values, species = data
>>> values
[5.8, 2.7, 5.1, 1.9]
>>> species
'virginica'
2.1.20. Use Case - 0x0C¶
Iris 2D
>>> 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 *values, species in DATA:
... avg = sum(values) / len(values)
... print(f'{avg=:.2f} {species=}')
avg=3.88 species='virginica'
avg=2.55 species='setosa'
avg=3.48 species='versicolor'
2.1.21. Assignments¶
"""
* Assignment: Star Assignment List
* Required: yes
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
English:
1. Separate ip address from host names
2. Use asterisk `*` notation
3. Run doctests - all must succeed
Polish:
1. Odseparuj adres ip od nazwy hostów
2. Skorzystaj z notacji z gwiazdką `*`
3. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> assert ip is not Ellipsis, \
'Assign your result to variable `ip`'
>>> assert hosts is not Ellipsis, \
'Assign your result to variable: `hosts`'
>>> assert type(ip) is str, \
'Variable `ip` has invalid type, should be str'
>>> assert type(hosts) is list, \
'Variable `hosts` has invalid type, should be list'
>>> assert all(type(x) is str for x in hosts), \
'All rows in `hosts` should be str'
>>> assert '' not in hosts, \
'Do not pass any arguments to str.split() method'
>>> ip
'10.13.37.1'
>>> hosts
['nasa.gov', 'esa.int', 'polsa.gov.pl']
"""
DATA = ['10.13.37.1', 'nasa.gov', 'esa.int', 'polsa.gov.pl']
# String with IP address
# example: '10.13.37.1'
# type: str
ip = ...
# List of hosts
# example: ['nasa.gov', 'esa.int', 'polsa.gov.pl']
# type: list[str]
hosts = ...
"""
* Assignment: Star Assignment Func
* Required: yes
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
English:
1. Separate ip address from host names
2. Use star expression
3. Run doctests - all must succeed
Polish:
1. Odseparuj adres ip od nazwy hostów
2. Użyj wyrażenia z gwiazdką
3. Uruchom doctesty - wszystkie muszą się powieść
Hints:
* `str.split()`
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> assert ip is not Ellipsis, \
'Assign your result to variable `ip`'
>>> assert hosts is not Ellipsis, \
'Assign your result to variable: `hosts`'
>>> assert type(ip) is str, \
'Variable `ip` has invalid type, should be str'
>>> assert type(hosts) is list, \
'Variable `hosts` has invalid type, should be list'
>>> assert all(type(x) is str for x in hosts), \
'All rows in `hosts` should be str'
>>> assert '' not in hosts, \
'Do not pass any arguments to str.split() method'
>>> ip
'10.13.37.1'
>>> hosts
['nasa.gov', 'esa.int', 'polsa.gov.pl']
"""
DATA = '10.13.37.1 nasa.gov esa.int polsa.gov.pl'
# String with IP address: '10.13.37.1'
# type: str
ip = ...
# List of host names: ['nasa.gov', 'esa.int', 'polsa.gov.pl']
# type: list[str]
hosts = ...
"""
* Assignment: Star Assignment Nested
* Required: yes
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
English:
1. Separate values from species name
2. Use star expression
3. Run doctests - all must succeed
Polish:
1. Odseparuj wartości od nazwy gatunku
2. Użyj wyrażenia z gwiazdką
3. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> assert values is not Ellipsis, \
'Assign result to variable: `values`'
>>> assert species is not Ellipsis, \
'Assign result to variable: `species`'
>>> assert len(values) > 0, \
'Variable `values` cannot be empty'
>>> assert len(species) > 0, \
'Variable `species` cannot be empty'
>>> assert type(values) is list, \
'Variable `values` has invalid type, should be list'
>>> assert type(species) is str, \
'Variable `species` has invalid type, should be str'
>>> assert all(type(x) is float for x in values), \
'All rows in `values` should be float'
>>> values
[5.1, 3.5, 1.4, 0.2]
>>> species
'setosa'
"""
DATA = (5.1, 3.5, 1.4, 0.2, 'setosa')
# All numeric values from DATA
# type: list[float]
values = ...
# species name from DATA (last element)
# type: str
species = ...
"""
* Assignment: Star Assignment Nested
* Required: yes
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min
English:
1. Separate header and rows
2. Use star expression
3. Run doctests - all must succeed
Polish:
1. Odseparuj nagłówek od wierszy danych
2. Użyj wyrażenia z gwiazdką
3. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> assert header is not Ellipsis, \
'Assign result to variable: `header`'
>>> assert rows is not Ellipsis, \
'Assign result to variable: `rows`'
>>> assert len(header) > 0, \
'Variable `header` cannot be empty'
>>> assert len(rows) > 0, \
'Variable `rows` cannot be empty'
>>> assert type(header) is tuple, \
'Variable `header` has invalid type, should be tuple'
>>> assert type(rows) is list, \
'Variable `hosts` has invalid type, should be list'
>>> assert all(type(x) is str for x in header), \
'All rows in `header` should be str'
>>> assert all(type(x) is tuple for x in rows), \
'All rows in `rows` should be tuple'
>>> header
('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species')
>>> rows # 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')]
"""
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'),
]
# first line from DATA
# example: ('sepal_length', 'sepal_width', ...)
# type: tuple[str]
header = ...
# all the other lines from DATA, beside first line
# example: [(5.8, 2.7, 5.1, 1.9, 'virginica'), ...]
# type: list[tuple]
rows = ...
"""
* Assignment: Star Assignment Loop
* Complexity: easy
* Lines of code: 4 lines
* Time: 5 min
English:
1. Iterate over data splitting `values` from `species`
2. Define `result: list[str]` with
species names ending with "ca" or "osa"
3. Use star expression
4. Run doctests - all must succeed
Polish:
1. Iteruj po danych rozdzielając `values` od `species`
2. Zdefiniuj `result: list[str]` z
nazwami gatunków kończącymi się na "ca" lub "osa"
3. Użyj wyrażenia z gwiazdką
4. Uruchom doctesty - wszystkie muszą się powieść
Hints:
* `str.endswith()`
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert len(result) > 0, \
'Variable `result` cannot be empty'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'
>>> assert all(type(x) is str for x in result), \
'All rows in `result` should be str'
>>> result
['virginica', 'setosa', 'virginica', 'setosa']
"""
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'),
]
SUFFIXES = ('ca', 'osa')
# species names ending with "ca" or "osa"
# type: list[str]
result = ...