3.1. Star Assignment
Arbitrary Number of Arguments
a, b, *c = 1, 2, 3, 4, 5Used 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
3.1.1. Recap
a = 1- inta = 1, 2- tuplea, b = 1, 2- multiple assignmenta, b, c = 1, 2, 3- multiple assignment
Example:
>>> firstname, lastname, email_work, email_school = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
>>>
>>> firstname
'Alice'
>>>
>>> lastname
'Apricot'
>>>
>>> email_work
'alice@example.com'
>>>
>>> email_school
'alice@example.edu'
3.1.2. Unpack Right
Unpack values at the right side
firstname, lastname, *emails = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
>>> firstname, lastname, *emails = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
>>>
>>> firstname
'Alice'
>>>
>>> lastname
'Apricot'
>>>
>>> emails
['alice@example.com', 'alice@example.edu']
3.1.3. Unpack Left
Unpack values at the left side
*name, email_work, email_school = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
>>> *name, email_work, email_school = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
>>>
>>> name
['Alice', 'Apricot']
>>>
>>> email_work
'alice@example.com'
>>>
>>> email_school
'alice@example.edu'
3.1.4. Unpack Middle
Unpack values from the middle
>>> firstname, *others, email_school = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
>>>
>>> firstname
'Alice'
>>>
>>> others
['Apricot', 'alice@example.com']
>>>
>>> email_school
'alice@example.edu'
3.1.5. Errors
Cannot unpack from both sides at once
Cannot unpack from both sides at once:
>>> *name, *email = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
Traceback (most recent call last):
SyntaxError: multiple starred expressions in assignment
3.1.6. Skipping Values
_is used to skip valuesIt is a regular variable name, not a special Python syntax
By convention it is used for data we don't want to access in future
It can be used multiple times in the same statement
>>> firstname, lastname, *_ = ('Alice', 'Apricot', 'alice@example.com', 'alice@example.edu')
>>>
>>> firstname
'Alice'
>>>
>>> lastname
'Apricot'
3.1.7. Multi Dimensional
Unpack values from multi-dimensional data
>>> DATA = [
... ('firstname', 'lastname', 'age'),
... ('Alice', 'Apricot', 30),
... ('Bob', 'Blackthorn', 31),
... ('Carol', 'Corn', 32),
... ('Dave', 'Durian', 33),
... ('Eve', 'Elderberry', 34),
... ('Mallory', 'Melon', 15),
... ]
Writing:
>>> header = DATA[0]
>>> rows = DATA[1:]
Has the same effect as:
>>> header, *rows = DATA
Results:
>>> header
('firstname', 'lastname', 'age')
>>>
>>> rows
[('Alice', 'Apricot', 30),
('Bob', 'Blackthorn', 31),
('Carol', 'Corn', 32),
('Dave', 'Durian', 33),
('Eve', 'Elderberry', 34),
('Mallory', 'Melon', 15)]
3.1.8. For Loop Unpacking
Use star expression to unpack values in for loop
>>> DATA = [
... ('firstname', 'lastname', 'age'),
... ('Alice', 'Apricot', 30),
... ('Bob', 'Blackthorn', 31),
... ('Carol', 'Corn', 32),
... ('Dave', 'Durian', 33),
... ('Eve', 'Elderberry', 34),
... ('Mallory', 'Melon', 15),
... ]
>>> for firstname, lastname, age in DATA[1:]:
... print(firstname)
...
Alice
Bob
Carol
Dave
Eve
Mallory
>>> for firstname, *_ in DATA[1:]:
... print(firstname)
...
Alice
Bob
Carol
Dave
Eve
Mallory
3.1.9. Recap
a, b, *c = 1, 2, 3, 4, 5Used 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
3.1.10. Case Study
# avg=3.4, species='virginica'
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'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
(4.6, 3.1, 1.5, 0.2, 'setosa'),
]
header, *rows = DATA
# header = DATA[0]
# rows = DATA[1:]
# avg=3.4, species='virginica'
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'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
(4.6, 3.1, 1.5, 0.2, 'setosa'),
]
header, *rows = DATA
for row in rows:
values = row[:-1]
species = row[-1]
avg = sum(values) / len(values)
print(f'{avg=:.1f}, {species=}')
# avg=3.4, species='virginica'
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'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
(4.6, 3.1, 1.5, 0.2, 'setosa'),
]
header, *rows = DATA
for row in rows:
*values, species = row
avg = sum(values) / len(values)
print(f'{avg=:.1f}, {species=}')
# avg=3.4, species='virginica'
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'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
(4.6, 3.1, 1.5, 0.2, 'setosa'),
]
header, *rows = DATA
for *values, species in rows:
avg = sum(values) / len(values)
print(f'{avg=:.1f}, {species=}')
3.1.11. Use Case - 1
>>> a, b, c = range(0, 3)
>>> a, b, c, d, e = range(0, 5)
>>> a, b, *c = range(0, 10)
3.1.12. Use Case - 1
>>> line = 'staff,alice,bob,carol,dave,eve'
>>> group, *members = line.split(',')
>>>
>>> print(f'{group=}, {members=}')
group='staff', members=['alice', 'bob', 'carol', 'dave', 'eve']
3.1.13. Use Case - 2
>>> 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]
3.1.14. Use Case - 3
>>> 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
3.1.15. Use Case - 4
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'
3.1.16. Use Case - 5
>>> *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'
3.1.17. Use Case - 6
>>> 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']
3.1.18. Use Case - 7
>>> line = 'alice:x:1000:1000:Alice:/home/alice:/bin/bash'
>>> username, password, uid, *others = line.split(':')
>>>
>>> username
'alice'
>>>
>>> password
'x'
>>>
>>> uid
'1000'
>>>
>>> others
['1000', 'Alice', '/home/alice', '/bin/bash']
3.1.19. Use Case - 8
>>> line = 'alice:x:1000:1000:Alice:/home/alice:/bin/bash'
>>> username, _, uid, *_ = line.split(':')
>>>
>>> username
'alice'
>>>
>>> uid
'1000'
3.1.20. Use Case - 9
>>> 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'
3.1.21. Use Case - 10
>>> data = (5.8, 2.7, 5.1, 1.9, 'virginica')
>>> *values, species = data
>>>
>>> values
[5.8, 2.7, 5.1, 1.9]
>>>
>>> species
'virginica'
3.1.22. Use Case - 11
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'
3.1.23. Assignments
# %% About
# - Name: Star Assignment List
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% English
# 1. Use star expression to separate IP address from hosts
# 2. Define variable `ip` with IP address
# 3. Define variable `hosts` with list of hosts
# 4. Run doctests - all must succeed
# %% Polish
# 1. Użyj wyrażenia z gwiazdką do odseparowania adresu ip od hostów
# 2. Zdefiniuj zmienną `ip` z adresem ip
# 3. Zdefiniuj zmienną `hosts` z listą hostów
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Expected
# >>> ip
# '127.0.0.1'
#
# >>> hosts
# ['example.com', 'example.net', 'example.org']
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> assert ip is not Ellipsis, \
'Variable `ip` has an invalid value; assign result of your program to it.'
>>> assert hosts is not Ellipsis, \
'Variable `hosts` has an invalid value; assign result of your program to it.'
>>> assert type(ip) is str, \
'Variable `ip` has an invalid type; expected: `str`.'
>>> assert type(hosts) is list, \
'Variable `hosts` has an invalid type; expected: `list`.'
>>> assert all(type(x) is str for x in hosts), \
'Variable `hosts` has elements of an invalid type; all items should be: `str`.'
>>> assert '' not in hosts, \
'Do not pass any arguments to str.split() method'
>>> ip
'127.0.0.1'
>>> hosts
['example.com', 'example.net', 'example.org']
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
# %% Types
ip: str
hosts: list[str]
# %% Data
DATA = ['127.0.0.1', 'example.com', 'example.net', 'example.org']
# %% Result
# %% About
# - Name: Star Assignment Func
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% English
# 0. Mind, this assignment is very similar to the previous one,
# but input data (`DATA`) is a bit different
# 1. Use star expression to separate IP address from hosts
# 2. Define variable `ip` with IP address
# 3. Define variable `hosts` with list of hosts
# 4. Run doctests - all must succeed
# %% Polish
# 0. Zwróć uwagę, że to zadanie jest bardzo podobne do poprzedniego,
# ale dane wejściowe (`DATA`) są trochę inne
# 1. Użyj wyrażenia z gwiazdką do odseparowania adresu ip od hostów
# 2. Zdefiniuj zmienną `ip` z adresem ip
# 3. Zdefiniuj zmienną `hosts` z listą hostów
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Expected
# >>> ip
# '127.0.0.1'
#
# >>> hosts
# ['example.com', 'example.net', 'example.org']
# %% Hints
# - `str.split()`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> assert ip is not Ellipsis, \
'Variable `ip` has an invalid value; assign result of your program to it.'
>>> assert hosts is not Ellipsis, \
'Variable `hosts` has an invalid value; assign result of your program to it.'
>>> assert type(ip) is str, \
'Variable `ip` has an invalid type; expected: `str`.'
>>> assert type(hosts) is list, \
'Variable `hosts` has an invalid type; expected: `list`.'
>>> assert all(type(x) is str for x in hosts), \
'Variable `hosts` has elements of an invalid type; all items should be: `str`.'
>>> assert '' not in hosts, \
'Do not pass any arguments to str.split() method'
>>> ip
'127.0.0.1'
>>> hosts
['example.com', 'example.net', 'example.org']
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
# %% Types
ip: str
hosts: list[str]
# %% Data
DATA = '127.0.0.1 example.com example.net example.org'
# %% Result
# %% About
# - Name: Star Assignment Nested
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% English
# 1. Use star expression to separate values from species name
# 2. Define variable `values` with numerical values
# 3. Define variable `species` with species name
# 4. Run doctests - all must succeed
# %% Polish
# 1. Użyj wyrażenia z gwiazdką do odseparowania wartości od nazwy gatunku
# 2. Zdefiniuj zmienną `values` z numerycznymi wartościami
# 3. Zdefiniuj zmienną `species` z nazwą gatunku
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Expected
# >>> values
# [5.1, 3.5, 1.4, 0.2]
#
# >>> species
# 'setosa'
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> assert values is not Ellipsis, \
'Variable `values` has an invalid value; assign result of your program to it.'
>>> assert species is not Ellipsis, \
'Variable `species` has an invalid value; assign result of your program to it.'
>>> assert len(values) > 0, \
'Variable `values` has an invalid length; expected more than zero elements.'
>>> assert len(species) > 0, \
'Variable `species` has an invalid length; expected more than zero elements.'
>>> assert type(values) is list, \
'Variable `values` has an invalid type; expected: `list`.'
>>> assert type(species) is str, \
'Variable `species` has an invalid type; expected: `str`.'
>>> assert all(type(x) is float for x in values), \
'Variable `values` has elements of an invalid type; all items should be: `float`.'
>>> values
[5.1, 3.5, 1.4, 0.2]
>>> species
'setosa'
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
# %% Types
values: list[float]
species: str
# %% Data
DATA = (5.1, 3.5, 1.4, 0.2, 'setosa')
# %% Result
# %% About
# - Name: Star Assignment Nested
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% English
# 1. Use star expression to separate header from rows
# 2. Define variable `header` with header (first row)
# 3. Define variable `rows` with all the other rows
# 4. Run doctests - all must succeed
# %% Polish
# 1. Użyj wyrażenia z gwiazdką do odseparowania nagłówka od wierszy
# 2. Zdefiniuj zmienną `header` z nagłówkiem (pierwszy wiersz)
# 3. Zdefiniuj zmienną `rows` z pozostałymi wierszami
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Expected
# >>> header
# ('firstname', 'lastname', 'age')
#
# >>> rows
# [('Alice', 'Apricot', 30),
# ('Bob', 'Blackthorn', 31),
# ('Carol', 'Corn', 32),
# ('Dave', 'Durian', 33),
# ('Eve', 'Elderberry', 34),
# ('Mallory', 'Melon', 15)]
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> assert header is not Ellipsis, \
'Variable `header` has an invalid value; assign result of your program to it.'
>>> assert rows is not Ellipsis, \
'Variable `rows` has an invalid value; assign result of your program to it.'
>>> assert len(header) > 0, \
'Variable `header` has an invalid length; expected more than zero elements.'
>>> assert len(rows) > 0, \
'Variable `rows` has an invalid length; expected more than zero elements.'
>>> assert type(header) is tuple, \
'Variable `header` has an invalid type; expected: `tuple`.'
>>> assert type(rows) is list, \
'Variable `rows` has an invalid type; expected: `list`.'
>>> assert all(type(x) is str for x in header), \
'Variable `header` has elements of an invalid type; all items should be: `str`.'
>>> assert all(type(x) is tuple for x in rows), \
'Variable `rows` has elements of an invalid type; all items should be: `tuple`.'
>>> header
('firstname', 'lastname', 'age')
>>> from pprint import pprint
>>> pprint(rows)
[('Alice', 'Apricot', 30),
('Bob', 'Blackthorn', 31),
('Carol', 'Corn', 32),
('Dave', 'Durian', 33),
('Eve', 'Elderberry', 34),
('Mallory', 'Melon', 15)]
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
# %% Types
header: tuple[str, str, str]
rows: list[tuple[str, str, str]]
# %% Data
DATA = [
('firstname', 'lastname', 'age'),
('Alice', 'Apricot', 30),
('Bob', 'Blackthorn', 31),
('Carol', 'Corn', 32),
('Dave', 'Durian', 33),
('Eve', 'Elderberry', 34),
('Mallory', 'Melon', 15),
]
# %% Result
# %% About
# - Name: Star Assignment Loop
# - Difficulty: easy
# - Lines: 4
# - Minutes: 5
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% English
# 1. Define `result: list[str]` with all email addresses having domain name in `DOMAINS`
# 2. Use star unpack in for loop
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `result: list[str]` z wszystkimi adresami email mającymi domenę z `DOMAINS`
# 2. Użyj rozpakowywania z gwiazdką w pętli for
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Expected
# >>> result
# ['alice@example.com',
# 'bob@example.com',
# 'carol@example.com',
# 'mallory@example.net']
# %% Hints
# - `str.endswith()`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> from pprint import pprint
>>> assert 'result' in globals(), \
'Variable `result` is not defined; assign result of your program to it.'
>>> assert result is not Ellipsis, \
'Variable `result` has an invalid value; assign result of your program to it.'
>>> assert type(result) is list, \
'Variable `result` has an invalid type; expected: `list`.'
>>> assert len(result) > 0, \
'Variable `result` has an invalid length; expected more than zero elements.'
>>> assert all(type(x) is str for x in result), \
'Variable `result` has elements of an invalid type; all items should be: `str`.'
>>> pprint(result)
['alice@example.com',
'bob@example.com',
'carol@example.com',
'mallory@example.net']
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
# %% Types
result: list[str]
# %% Data
DATA = [
('firstname', 'lastname', 'email'),
('Alice', 'Apricot', 'alice@example.com'),
('Bob', 'Blackthorn', 'bob@example.com'),
('Carol', 'Corn', 'carol@example.com'),
('Dave', 'Durian', 'dave@example.org'),
('Eve', 'Elderberry', 'eve@example.org'),
('Mallory', 'Melon', 'mallory@example.net'),
]
DOMAINS = ('example.com', 'example.net')
# %% Result
result = ...