3.4. Star Arguments
Unpack and Arbitrary Number of Parameters and Arguments
*
is used for positional arguments**
is used for keyword argumentsfunction(*args)
- unpacks from sequence (tuple
,list
,set
, etc)function(**kwargs)
- unpacks from mapping (dict
, etc)function(*args, **kwargs)
- unpacks from sequence and mappings

3.4.1. Recap
Argument - value passed to the function
Argument can be: positional or keyword
Positional arguments - resolved by position, order is important, must be at the left side
Keyword arguments - resolved by name, order is not important, must be on the right side
After first keyword argument, all following arguments must also be keyword
>>> def login(username, password, remember=False):
... ...
Positional arguments (order is important):
>>> login('alice', 'secret', True)
Keyword arguments (order is not important):
>>> login(username='alice', password='secret', remember=True)
Positional and keyword arguments:
>>> login('alice', 'secret', remember=True)
3.4.2. Positional Arguments
function(*args)
- unpacks from sequence (tuple
,list
,set
, etc)*
is used for positional argumentsThere is no convention, so you can use any name, for example
*args
>>> def login(username, password, remember=False):
... print(f'{username=}, {password=}, {remember=}')
>>>
>>> args = ('alice', 'secret', True)
Without star unpacking:
>>> login(args[0], args[1], args[2])
username='alice', password='secret', remember=True
With start unpacking:
>>> login(*args)
username='alice', password='secret', remember=True
3.4.3. Keyword Arguments
function(**kwargs)
- unpacks from mapping (dict
, etc)**
is used for keyword argumentsThere is no convention, so you can use any name, for example
**kwargs
Keyword arguments passed directly:
>>> def login(username, password, remember=False):
... print(f'{username=}, {password=}, {remember=}')
>>>
>>> kwargs = {'username': 'alice', 'password': 'secret', 'remember': True}
Without star unpacking:
>>> login(username=kwargs['username'], password=kwargs['password'], remember=kwargs['remember'])
username='alice', password='secret', remember=True
With start unpacking:
>>> login(**kwargs)
username='alice', password='secret', remember=True
3.4.4. Positional and Keyword Arguments
function(*args, **kwargs)
- unpacks from sequence and mappings*
is used for positional arguments**
is used for keyword argumentsThere is no convention, so you can use any name, for example
*args
There is no convention, so you can use any name, for example
**kwargs
>>> def login(username, password, remember=False):
... print(f'{username=}, {password=}, {remember=}')
>>>
>>> args = ('alice', 'secret')
>>> kwargs = {'remember': True}
Without star unpacking:
>>> login(args[0], args[1], remember=kwargs['remember'])
username='alice', password='secret', remember=True
With star unpacking:
>>> login(*args, **kwargs)
username='alice', password='secret', remember=True
3.4.5. Merge Kwargs
function(**kwargs1, **kwargs2)
>>> def login(username, password, remember=False):
... print(f'{username=}, {password=}, {remember=}')
>>>
>>> kwargs1 = {'username': 'alice', 'password': 'secret'}
>>> kwargs2 = {'remember': True}
With star unpacking:
>>> login(**kwargs1, **kwargs2)
username='alice', password='secret', remember=True
3.4.6. Merge Dicts
dict(**data1, **data2)
- old way{**data1, **data2}
- old waydata1 | data2
- since Python 3.9 preferred way
>>> data1 = {'a':1, 'b':2}
>>> data2 = {'c':3, 'd':4}
Before Python 3.9 merging dicts was done with dict
or {}
:
>>> dict(**data1, **data2)
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> {**data1, **data2}
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
Since Python 3.9 there is a dedicated operator for merging dicts:
>>> data1 | data2
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
3.4.7. Create Objects
One object from sequence
One object from mapping
Many objects from sequence of sequences
Many objects from sequence of mappings
>>> class User:
... def __init__(self, firstname, lastname):
... self.firstname = firstname
... self.lastname = lastname
...
... def __repr__(self):
... return f"User(firstname='{self.firstname}', lastname='{self.lastname}')"
One object from sequence:
>>> data = ('Alice', 'Apricot')
>>>
>>> result = User(*data)
>>> result
User(firstname='Alice', lastname='Apricot')
One object from mapping:
>>> data = {'firstname': 'Alice', 'lastname': 'Apricot'}
>>>
>>> result = User(**data)
>>> result
User(firstname='Alice', lastname='Apricot')
Many objects from sequence of sequences:
>>> data = [
... ('Alice', 'Apricot'),
... ('Bob', 'Banana'),
... ('Carol', 'Corn'),
... ('Dave', 'Durian'),
... ('Eve', 'Elderberry'),
... ('Mallory', 'Melon'),
... ]
>>>
>>> result = [User(*row) for row in data]
>>> result
[User(firstname='Alice', lastname='Apricot'),
User(firstname='Bob', lastname='Banana'),
User(firstname='Carol', lastname='Corn'),
User(firstname='Dave', lastname='Durian'),
User(firstname='Eve', lastname='Elderberry'),
User(firstname='Mallory', lastname='Melon')]
Many objects from sequence of mappings:
>>> data = [
... {'firstname': 'Alice', 'lastname': 'Apricot'},
... {'firstname': 'Bob', 'lastname': 'Banana'},
... {'firstname': 'Carol', 'lastname': 'Corn'},
... {'firstname': 'Dave', 'lastname': 'Durian'},
... {'firstname': 'Eve', 'lastname': 'Elderberry'},
... {'firstname': 'Mallory', 'lastname': 'Melon'},
... ]
>>>
>>> result = [User(**row) for row in data]
>>> result
[User(firstname='Alice', lastname='Apricot'),
User(firstname='Bob', lastname='Banana'),
User(firstname='Carol', lastname='Corn'),
User(firstname='Dave', lastname='Durian'),
User(firstname='Eve', lastname='Elderberry'),
User(firstname='Mallory', lastname='Melon')]
3.4.8. Recap
*
is used for positional arguments**
is used for keyword argumentsecho(*data)
- unpacks from sequence (tuple
,list
,set
, etc)echo(**data)
- unpacks from mapping (dict
, etc)echo(*data1, **data2)
- unpacks from sequence and mappingsCreate one object from sequence
Create one object from mapping
Create many objects from sequence of sequences
Create many objects from sequence of mappings
Old way to merge dicts
dict(**data1, **data2)
or{**data1, **data2}
Since Python 3.9 merge dicts with:
data1 | data2
3.4.9. Use Case - 1
>>> def database_connect(host, port, username, password, database):
... ...
After reading config from file we have a dict:
>>> CONFIG = {
... 'host': 'example.com',
... 'port': 1234,
... 'username': 'myusername',
... 'password': 'mypassword',
... 'database': 'mydatabase'}
Database connection configuration read from config file:
>>> connection = database_connect(
... host=CONFIG['host'],
... port=CONFIG['port'],
... username=CONFIG['username'],
... password=CONFIG['password'],
... database=CONFIG['database'])
Or:
>>> connection = database_connect(**CONFIG)
3.4.10. Use Case - 2
Calling a function which has similar parameters. Passing configuration to the function, which sets parameters from the config:
>>> def draw_line(x, y, color, type, width, markers):
... ...
>>> draw_line(x=1, y=2, color='red', type='dashed', width='2px', markers='disc')
>>> draw_line(x=3, y=4, color='red', type='dashed', width='2px', markers='disc')
>>> draw_line(x=5, y=6, color='red', type='dashed', width='2px', markers='disc')
>>> style = {
... 'color': 'red',
... 'type': 'dashed',
... 'width': '2px',
... 'markers': 'disc',
... }
>>>
>>> draw_line(x=1, y=2, **style)
>>> draw_line(x=3, y=4, **style)
>>> draw_line(x=5, y=6, **style)
3.4.11. Use Case - 3
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Iris:
... sepal_length: float
... sepal_width: float
... petal_length: float
... petal_width: float
... species: str
>>> 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'),
... (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'),
... ]
>>>
>>> result = [Iris(*row) for row in DATA]
>>> print(result)
[Iris(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9, species='virginica'),
Iris(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species='setosa'),
Iris(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3, species='versicolor'),
Iris(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8, species='virginica'),
Iris(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5, species='versicolor'),
Iris(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2, species='setosa')]
>>> DATA = [
... {"sepal_length":5.8,"sepal_width":2.7,"petal_length":5.1,"petal_width":1.9,"species":"virginica"},
... {"sepal_length":5.1,"sepal_width":3.5,"petal_length":1.4,"petal_width":0.2,"species":"setosa"},
... {"sepal_length":5.7,"sepal_width":2.8,"petal_length":4.1,"petal_width":1.3,"species":"versicolor"},
... {"sepal_length":6.3,"sepal_width":2.9,"petal_length":5.6,"petal_width":1.8,"species":"virginica"},
... {"sepal_length":6.4,"sepal_width":3.2,"petal_length":4.5,"petal_width":1.5,"species":"versicolor"},
... {"sepal_length":4.7,"sepal_width":3.2,"petal_length":1.3,"petal_width":0.2,"species":"setosa"},
... ]
>>>
>>> result = [Iris(**row) for row in DATA]
>>> print(result)
[Iris(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9, species='virginica'),
Iris(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species='setosa'),
Iris(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3, species='versicolor'),
Iris(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8, species='virginica'),
Iris(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5, species='versicolor'),
Iris(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2, species='setosa')]
3.4.12. Use Case - 5
Definition of
pandas.read_csv()
function [1]Proxy functions. One of the most common use of
*args
,**kwargs
:
>>> def read_csv(filepath_or_buffer, /, *, sep=', ', delimiter=None,
... header='infer', names=None, index_col=None, usecols=None,
... squeeze=False, prefix=None, mangle_dupe_cols=True,
... dtype=None, engine=None, converters=None, true_values=None,
... false_values=None, skipinitialspace=False, skiprows=None,
... nrows=None, na_values=None, keep_default_na=True,
... na_filter=True, verbose=False, skip_blank_lines=True,
... parse_dates=False, infer_datetime_format=False,
... keep_date_col=False, date_parser=None, dayfirst=False,
... iterator=False, chunksize=None, compression='infer',
... thousands=None, decimal=b'.', lineterminator=None,
... quotechar='"', quoting=0, escapechar=None, comment=None,
... encoding=None, dialect=None, tupleize_cols=None,
... error_bad_lines=True, warn_bad_lines=True, skipfooter=0,
... doublequote=True, delim_whitespace=False, low_memory=True,
... memory_map=False, float_precision=None): ...
Calling function with positional only arguments is insane. In Python we don't do that, because we have keyword arguments.
>>> read_csv('/tmp/myfile.csv', ';', None, 'infer', None, None, None, False,
... True, None, None, None, None, None, False, None, None, None,
... None, True, True, False, True, False, False, False, None, False,
... False, None, 'infer', None, b',', None, '"', 0, None, None,
... None, None, None, True, True, 0, True, False, True, False, None)
Traceback (most recent call last):
TypeError: read_csv() takes 1 positional argument but 49 were given
Keyword arguments with sensible defaults are your best friends. The number of function parameters suddenly is not a problem:
>>> read_csv('myfile1.csv', delimiter=';', decimal=b',')
>>> read_csv('myfile2.csv', delimiter=';', decimal=b',')
>>> read_csv('myfile3.csv', delimiter=';', decimal=b',')
>>> read_csv('myfile4.csv', delimiter=';', decimal=b',')
>>> read_csv('myfile5.csv', delimiter=';', decimal=b',')
Proxy functions allows for changing defaults to the original function. One simply define a function which has sensible defaults and call the original function setting default values automatically:
>>> def mycsv(file, delimiter=';', decimal=b',', **kwargs):
... return read_csv(file, delimiter=delimiter, decimal=decimal, **kwargs)
Thanks to using **kwargs
there is no need to specify all the values
from the original function. The uncovered arguments will simply be put
in kwargs
dictionary and passed to the original function:
>>> mycsv('/tmp/myfile1.csv')
>>> mycsv('/tmp/myfile2.csv')
>>> mycsv('/tmp/myfile3.csv')
>>> mycsv('/tmp/myfile4.csv')
>>> mycsv('/tmp/myfile5.csv')
This allows for cleaner code. Each parameter will be passed to mycsv
function. Then it will be checked if there is a different default value
already defined. If not, then parameter will be stored in kwargs
and
passed to the original function:
>>> mycsv('/tmp/myfile.csv', encoding='utf-8')
>>> mycsv('/tmp/myfile.csv', encoding='utf-8', verbose=True)
>>> mycsv('/tmp/myfile.csv', verbose=True, usecols=['sepal_length', 'species'])
3.4.13. Use Case - 6
Decorators are functions, which get reference to the decorated function as it's argument, and has closure which gets original function arguments as positional and keyword arguments:
>>> def mydecorator(func):
... def wrapper(*args, **kwargs):
... return func(*args, **kwargs)
... return wrapper
Decorators could be used on any function, therefore we could not predict what would be the name of the parameter passed to it:
>>> @mydecorator
... def add(a, b):
... return a + b
>>> @mydecorator
... def echo(text):
... return text
Moreover it depends on a user whether he/she chooses to run function positionally, using keyword arguments or even both at the same time:
>>> add(1, 2)
3
>>> add(a=1, b=2)
3
>>> add(1, b=2)
3
>>> echo('hello')
'hello'
3.4.14. References
3.4.15. Assignments
# %% About
# - Name: Star Arguments Args
# - 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 create `User` from `DATA`
# 2. Define variable `result` with `User` object
# 3. Run doctests - all must succeed
# %% Polish
# 1. Użyj wyrażenia z gwiazdką do stworzenia `User` z `DATA`
# 2. Zdefiniuj zmienną `result` z obiektem `User`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Example
# >>> result
# User(firstname='Alice', lastname='Apricot', email='alice@example.com')
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is User, \
'Variable `result` has invalid type, should be User'
>>> result # doctest: +NORMALIZE_WHITESPACE
User(firstname='Alice', lastname='Apricot', email='alice@example.com')
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% Imports
# %% Types
result: 'User'
# %% Data
class User:
def __init__(self, firstname, lastname, email):
self.firstname = firstname
self.lastname = lastname
self.email = email
def __repr__(self):
clsname = self.__class__.__name__
firstname = self.firstname
lastname = self.lastname
email = self.email
return f'{clsname}({firstname=}, {lastname=}, {email=})'
DATA = ('Alice', 'Apricot', 'alice@example.com')
# %% Result
# %% About
# - Name: Star Arguments Args
# - 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 create `list[User]` from `DATA`
# 2. Define variable `result` with `list[User]` objects
# 3. Run doctests - all must succeed
# %% Polish
# 1. Użyj wyrażenia z gwiazdką do stworzenia `list[User]` z `DATA`
# 2. Zdefiniuj zmienną `result` z obiektami `list[User]`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Example
# >>> result
# [User(firstname='Alice', lastname='Apricot', email='alice@example.com'),
# User(firstname='Bob', lastname='Banana', email='bob@example.com'),
# User(firstname='Carol', lastname='Corn', email='carol@example.com'),
# User(firstname='Dave', lastname='Durian', email='dave@example.org'),
# User(firstname='Eve', lastname='Elderberry', email='eve@example.org'),
# User(firstname='Mallory', lastname='Melon', email='mallory@example.net')]
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'
>>> assert all(type(row) is User for row in result), \
'All elements in result must be a User'
>>> from pprint import pprint
>>> pprint(result) # doctest: +NORMALIZE_WHITESPACE
[User(firstname='Alice', lastname='Apricot', email='alice@example.com'),
User(firstname='Bob', lastname='Banana', email='bob@example.com'),
User(firstname='Carol', lastname='Corn', email='carol@example.com'),
User(firstname='Dave', lastname='Durian', email='dave@example.org'),
User(firstname='Eve', lastname='Elderberry', email='eve@example.org'),
User(firstname='Mallory', lastname='Melon', email='mallory@example.net')]
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% Imports
# %% Types
result: list['User']
# %% Data
class User:
def __init__(self, firstname, lastname, email):
self.firstname = firstname
self.lastname = lastname
self.email = email
def __repr__(self):
clsname = self.__class__.__name__
firstname = self.firstname
lastname = self.lastname
email = self.email
return f'{clsname}({firstname=}, {lastname=}, {email=})'
DATA = [
('firstname', 'lastname', 'email'),
('Alice', 'Apricot', 'alice@example.com'),
('Bob', 'Banana', 'bob@example.com'),
('Carol', 'Corn', 'carol@example.com'),
('Dave', 'Durian', 'dave@example.org'),
('Eve', 'Elderberry', 'eve@example.org'),
('Mallory', 'Melon', 'mallory@example.net'),
]
# %% Result
# %% About
# - Name: Star Arguments Kwargs
# - 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 create `User` from `DATA`
# 2. Define variable `result` with `User` object
# 3. Run doctests - all must succeed
# %% Polish
# 1. Użyj wyrażenia z gwiazdką do stworzenia `User` z `DATA`
# 2. Zdefiniuj zmienną `result` z obiektem `User`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Example
# >>> result
# User(firstname='Alice', lastname='Apricot', email='alice@example.com')
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is User, \
'Variable `result` has invalid type, should be User'
>>> result # doctest: +NORMALIZE_WHITESPACE
User(firstname='Alice', lastname='Apricot', email='alice@example.com')
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% Imports
# %% Types
result: 'User'
# %% Data
class User:
def __init__(self, firstname, lastname, email):
self.firstname = firstname
self.lastname = lastname
self.email = email
def __repr__(self):
clsname = self.__class__.__name__
firstname = self.firstname
lastname = self.lastname
email = self.email
return f'{clsname}({firstname=}, {lastname=}, {email=})'
DATA = {'firstname':'Alice', 'lastname':'Apricot', 'email':'alice@example.com'}
# %% Result
# %% About
# - Name: Star Arguments Kwargs
# - 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 create `list[User]` from `DATA`
# 2. Define variable `result` with `list[User]` objects
# 3. Run doctests - all must succeed
# %% Polish
# 1. Użyj wyrażenia z gwiazdką do stworzenia `list[User]` z `DATA`
# 2. Zdefiniuj zmienną `result` z obiektami `list[User]`
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Example
# >>> result
# [User(firstname='Alice', lastname='Apricot', email='alice@example.com'),
# User(firstname='Bob', lastname='Banana', email='bob@example.com'),
# User(firstname='Carol', lastname='Corn', email='carol@example.com'),
# User(firstname='Dave', lastname='Durian', email='dave@example.org'),
# User(firstname='Eve', lastname='Elderberry', email='eve@example.org'),
# User(firstname='Mallory', lastname='Melon', email='mallory@example.net')]
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'
>>> assert all(type(row) is User for row in result), \
'All elements in result must be a User'
>>> from pprint import pprint
>>> pprint(result) # doctest: +NORMALIZE_WHITESPACE
[User(firstname='Alice', lastname='Apricot', email='alice@example.com'),
User(firstname='Bob', lastname='Banana', email='bob@example.com'),
User(firstname='Carol', lastname='Corn', email='carol@example.com'),
User(firstname='Dave', lastname='Durian', email='dave@example.org'),
User(firstname='Eve', lastname='Elderberry', email='eve@example.org'),
User(firstname='Mallory', lastname='Melon', email='mallory@example.net')]
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% Imports
# %% Types
result: list['User']
# %% Data
class User:
def __init__(self, firstname, lastname, email):
self.firstname = firstname
self.lastname = lastname
self.email = email
def __repr__(self):
clsname = self.__class__.__name__
firstname = self.firstname
lastname = self.lastname
email = self.email
return f'{clsname}({firstname=}, {lastname=}, {email=})'
DATA = [
{'firstname': 'Alice', 'lastname': 'Apricot', 'email': 'alice@example.com'},
{'firstname': 'Bob', 'lastname': 'Banana', 'email': 'bob@example.com'},
{'firstname': 'Carol', 'lastname': 'Corn', 'email': 'carol@example.com'},
{'firstname': 'Dave', 'lastname': 'Durian', 'email': 'dave@example.org'},
{'firstname': 'Eve', 'lastname': 'Elderberry', 'email': 'eve@example.org'},
{'firstname': 'Mallory', 'lastname': 'Melon', 'email': 'mallory@example.net'},
]
# %% Result