3.3. Star Parameters

  • * is used for positional parameters

  • args is a convention, but you can use any name

  • *args unpacks to tuple

  • ** is used for keyword parameters

  • kwargs is a convention, but you can use any name

  • **kwargs unpacks to dict

../../_images/unpack-assignment%2Cargs%2Cparams.png

3.3.1. Syntax

>>> def myfunction(*args):
...     pass
>>> def myfunction(**kwargs):
...     pass
>>> def myfunction(*args, **kwargs):
...     pass

3.3.2. Recap

  • Parameter - variable in function signature (function definition)

  • Parameter can be: required or optional

  • Required parameters - necessary to function call

  • Optional parameters - has default value

  • After first optional parameter, all following parameters must also be optional

Required parameters:

>>> def echo(a, b):
...     ...

Optional parameters:

>>> def echo(a=1, b=2):
...     ...

Required and optional parameters:

>>> def echo(a, b=2):
...     ...

Required parameters must be the leftmost:

>>> def echo(a=1, b):
...     ...
Traceback (most recent call last):
SyntaxError: parameter without a default follows parameter with a default

3.3.3. Positional Parameters

  • * is used for positional parameters

  • args is a convention, but you can use any name

  • *args unpacks to tuple

>>> def echo(*args):
...     print(f'{args=}')
>>>
>>>
>>> echo()
args=()
>>>
>>> echo(1)
args=(1,)
>>>
>>> echo(2, 3)
args=(2, 3)
>>>
>>> echo(1, 2, 3, 4, 5)
args=(1, 2, 3, 4, 5)

3.3.4. Keyword Parameters

  • ** is used for keyword parameters

  • kwargs is a convention, but you can use any name

  • **kwargs unpacks to dict

>>> def echo(**kwargs):
...     print(f'{kwargs=}')
>>>
>>>
>>> echo()
kwargs={}
>>>
>>> echo(a=1)
kwargs={'a': 1}
>>>
>>> echo(a=1, b=2)
kwargs={'a': 1, 'b': 2}
>>>
>>> echo(a=1, b=2, c=3, d=4, e=5)
kwargs={'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

3.3.5. Positional and Keyword Parameters

  • * is used for positional parameters

  • ** is used for keyword parameters

  • *args unpacks to tuple

  • **kwargs unpacks to dict

>>> def echo(*args, **kwargs):
...     print(f'{args=}, {kwargs=}')
>>>
>>>
>>> echo()
args=(), kwargs={}
>>>
>>> echo(1, 2, 3)
args=(1, 2, 3), kwargs={}
>>>
>>> echo(d=4, e=5, f=6)
args=(), kwargs={'d': 4, 'e': 5, 'f': 6}
>>>
>>> echo(1, 2, 3, d=4, e=5, f=6)
args=(1, 2, 3), kwargs={'d': 4, 'e': 5, 'f': 6}

3.3.6. Parameters with Args, Kwargs

>>> def echo(a, b, c=None, *args):
...     print(f'{a=}, {b=}, {c=}, {args=}')
>>>
>>>
>>> echo(1, 2)
a=1, b=2, c=None, args=()
>>>
>>> echo(1, 2, 3)
a=1, b=2, c=3, args=()
>>>
>>> echo(1, 2, 3, 4)
a=1, b=2, c=3, args=(4,)
>>>
>>> echo(1, 2, 3, 4, 5, 6)
a=1, b=2, c=3, args=(4, 5, 6)
>>> def echo(a, b, c=None, **kwargs):
...     print(f'{a=}, {b=}, {c=}, {kwargs=}')
>>>
>>>
>>> echo(1, 2)
a=1, b=2, c=None, kwargs={}
>>>
>>> echo(1, 2, 3)
a=1, b=2, c=3, kwargs={}
>>>
>>> echo(1, 2, 3, d=7, e=8, f=9)
a=1, b=2, c=3, kwargs={'d': 7, 'e': 8, 'f': 9}
>>>
>>> echo(1, 2, a=7)
Traceback (most recent call last):
TypeError: echo() got multiple values for argument 'a'
>>> def echo(a, b, c=None, *args, **kwargs):
...     print(f'{a=}, {b=}, {c=}, {args=}, {kwargs=}')
>>>
>>>
>>> echo(1, 2)
a=1, b=2, c=None, args=(), kwargs={}
>>>
>>> echo(1, 2, 3, 4, 5, 6)
a=1, b=2, c=3, args=(4, 5, 6), kwargs={}
>>>
>>> echo(1, 2, 3, d=7, e=8, f=9)
a=1, b=2, c=3, args=(), kwargs={'d': 7, 'e': 8, 'f': 9}
>>>
>>> echo(1, 2, 3, 4, 5, 6, d=7, e=8, f=9)
a=1, b=2, c=3, args=(4, 5, 6), kwargs={'d': 7, 'e': 8, 'f': 9}

3.3.7. Case Study

class ContextMixin:
    extra_context = None

    def get_context_data(self, **kwargs): ...
class View:
    http_method_names = ["get", "post", "put",  "delete"]

    def __init__(self, **kwargs): ...
    def view_is_async(cls): ...
    def as_view(cls, **initkwargs): ...
    def setup(self, request, *args, **kwargs): ...
    def dispatch(self, request, *args, **kwargs): ...
    def http_method_not_allowed(self, request, *args, **kwargs): ...
    def options(self, request, *args, **kwargs): ...
    def _allowed_methods(self): ...
from django.views.generic.base import TemplateResponse


class TemplateResponseMixin:
    template_name = None
    template_engine = None
    response_class = TemplateResponse
    content_type = None

    def render_to_response(self, context, **response_kwargs): ...
    def get_template_names(self): ...
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View


class TemplateView(TemplateResponseMixin, ContextMixin, View):
    def get(self, request, *args, **kwargs): ...
from django.views import View


class RedirectView(View):
    permanent = False
    url = None
    pattern_name = None
    query_string = False

    def get_redirect_url(self, *args, **kwargs): ...
    def get(self, request, *args, **kwargs): ...
    def head(self, request, *args, **kwargs): ...
    def post(self, request, *args, **kwargs): ...
    def options(self, request, *args, **kwargs): ...
    def delete(self, request, *args, **kwargs): ...
    def put(self, request, *args, **kwargs): ...
    def patch(self, request, *args, **kwargs): ...

3.3.8. Use Case - 1

>>> def add(*values):
...     total = 0
...     for value in values:
...         total += value
...     return total
>>>
>>>
>>> add()
0
>>>
>>> add(1)
1
>>>
>>> add(1, 4)
5
>>>
>>> add(3, 1)
4
>>>
>>> add(1, 2, 3, 4)
10

3.3.9. Use Case - 2

>>> def celsius_to_kelvin(*degrees):
...     return [x+273.15 for x in degrees]
>>>
>>>
>>> celsius_to_kelvin(1)
[274.15]
>>>
>>> celsius_to_kelvin(1, 2, 3, 4, 5)
[274.15, 275.15, 276.15, 277.15, 278.15]

3.3.10. Use Case - 3

>>> def html_list(*fruits):
...     print('<ul>')
...     for fruit in fruits:
...         print(f'<li>{fruit}</li>')
...     print('</ul>')
>>>
>>>
>>> html_list('apple', 'banana', 'orange')
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>

3.3.11. Use Case - 4

Intuitive definition of print function:

>>> print('hello')
hello
>>>
>>> print('hello', 'world')
hello world
>>>
>>> print('hello', 'new', 'world')
hello new world
>>> def print(*values, sep=' ', end='\n'):
...     return sep.join(values) + end

3.3.12. Assignments

# %% 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

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Star Parameters Define
# - Difficulty: easy
# - Lines: 4
# - Minutes: 5

# %% English
# 1. Create function `mean()`, which calculates arithmetic mean
# 2. If there is no arguments, raise ValueError with message:
#    'At least one argument is required'
# 3. Function can have arbitrary number of positional arguments
# 4. Non-functional requirements:
#    - Do not import any libraries and modules
#    - Use builtin functions `sum()` and `len()`
# 5. Run doctests - all must succeed

# %% Polish
# 1. Napisz funkcję `mean()`, wyliczającą średnią arytmetyczną
# 2. Jeżeli nie podano arumentów, podnieś wyjątek ValueError z wiadomością:
#    'At least one argument is required'
# 3. Funkcja przyjmuje dowolną ilość pozycyjnych argumentów
# 4. Wymagania niefunkcjonalne:
#    - Nie importuj żadnych bibliotek i modułów
#    - Użyj wbudowanych funckji `sum()` i `len()`
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `raise ValueError('error message')`
# - `sum(...) / len(...)`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> mean(1)
1.0
>>> mean(1, 2)
1.5
>>> mean(1, 2, 3)
2.0
>>> mean(1, 2, 3, 4)
2.5
>>> mean()
Traceback (most recent call last):
ValueError: At least one argument is required
"""

# Create function `mean()`, which calculates arithmetic mean
# If there is no arguments, raise ValueError with message:
# 'At least one argument is required'
def mean():
    ...


# %% 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

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Star Parameters Args
# - Difficulty: easy
# - Lines: 6
# - Minutes: 8

# %% English
# 1. Define function `isnumeric()`
#    - Function takes arbitrary number of positional arguments
#    - Arguments can be of any type
#    - Return `True` if all arguments are `int` or `float` only
#    - Return `False` if any argument is different type
# 2. Compare using `type()` and `isinstance()`
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj funkcję `isnumeric()`
#    - Funkcja przyjmuje dowolną liczbę argumentów pozycyjnych
#    - Podawane argumenty mogą być dowolnego typu
#    - Zwróć `True` jeżeli wszystkie argumenty są tylko typów `int` lub `float`
#    - Zwróć `False` jeżeli którykolwiek jest innego typu
# 2. Porównaj użycie `type()` i `isinstance()`
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `isinstance(obj, type1|type2)`
# - `type(obj)`
# - `... in tuple()`
# - `raise TypeError('error message')`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isfunction
>>> assert isfunction(isnumeric), \
'isnumeric must be a function'

>>> isnumeric()
Traceback (most recent call last):
TypeError: At least one argument is required

>>> isnumeric(1)
True
>>> isnumeric(1.0)
True
>>> isnumeric('one')
False
>>> isnumeric(True)
False

>>> isnumeric(1, 1.0)
True
>>> isnumeric(1,  1.0, 'one')
False
"""


# Return True if all arguments are int or float, otherwise False
# type: Callable[[int|float],bool]
def isnumeric(*args):
    ...


# %% 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

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Star Parameters Kwargs
# - Difficulty: medium
# - Lines: 7
# - Minutes: 8

# %% English
# 1. Define function `isnumeric()`
#    - Function takes arbitrary number of positional and keyword arguments
#    - Arguments can be of any type
#    - Return `True` if all arguments are `int` or `float` only
#    - Return `False` if any argument is different type
# 2. Compare using `type()` and `isinstance()`
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj funkcję `isnumeric()`
#    - Funkcja przyjmuje dowolną liczbę argumentów pozycyjnych i nazwanych
#    - Podawane argumenty mogą być dowolnego typu
#    - Zwróć `True` jeżeli wszystkie argumenty są tylko typów `int` lub `float`
#    - Zwróć `False` jeżeli którykolwiek jest innego typu
# 2. Porównaj użycie `type()` i `isinstance()`
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `isinstance(obj, type1|type2)`
# - `type(obj)`
# - `dict.values()`
# - `tuple() += tuple()`
# - `raise TypeError('error message')`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isfunction
>>> assert isfunction(isnumeric), \
'isnumeric must be a function'

>>> isnumeric()
Traceback (most recent call last):
TypeError: At least one argument is required

>>> isnumeric(1)
True
>>> isnumeric(1.0)
True
>>> isnumeric('one')
False
>>> isnumeric(True)
False

>>> isnumeric(1, 1.0)
True
>>> isnumeric(1,  1.0, 'one')
False

>>> isnumeric(a=1)
True
>>> isnumeric(a=1.0)
True
>>> isnumeric(a='one')
False
>>> isnumeric(a=True)
False

>>> isnumeric(a=1, b=1.0)
True
>>> isnumeric(a=1,  b=1.0, c='one')
False
"""


# Return True if all arguments are int or float, otherwise False
# type: Callable[[int|float],bool]
def isnumeric(*args, **kwargs):
    ...