3.3. Star Parameters
Variadic function
*
is used for positional parametersargs
is a convention, but you can use any name*args
unpacks totuple
**
is used for keyword parameterskwargs
is a convention, but you can use any name**kwargs
unpacks todict

3.3.1. Syntax
>>> def login(*args):
... return ...
>>> def login(**kwargs):
... return ...
>>> def login(*args, **kwargs):
... return ...
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 login(username, password):
... ...
Optional parameters:
>>> def login(username=None, password=None):
... ...
Required and optional parameters:
>>> def login(username, password=None):
... ...
3.3.3. Positional Parameters
*
is used for positional parametersargs
is a convention, but you can use any name*args
unpacks totuple
>>> def login(username, password, *args):
... print(f'{username=}, {password=}, {args=}')
>>>
>>>
>>> login('alice', 'secret')
username='alice', password='secret', args=()
>>>
>>> login('alice', 'secret', True)
username='alice', password='secret', args=(True,)
>>>
>>> login('alice', 'secret', True, 3600)
username='alice', password='secret', args=(True, 3600)
3.3.4. Keyword Parameters
**
is used for keyword parameterskwargs
is a convention, but you can use any name**kwargs
unpacks todict
>>> def login(username, password, **kwargs):
... print(f'{username=}, {password=}, {kwargs=}')
>>>
>>>
>>> login('alice', 'secret')
username='alice', password='secret', kwargs={}
>>>
>>> login('alice', 'secret', remember_me=True)
username='alice', password='secret', kwargs={'remember_me': True}
>>>
>>> login('alice', 'secret', remember_me=True, timeout=3600)
username='alice', password='secret', kwargs={'remember_me': True, 'timeout': 3600}
3.3.5. Positional and Keyword Parameters
*
is used for positional parameters**
is used for keyword parameters*args
unpacks totuple
**kwargs
unpacks todict
>>> def login(username, password, *args, **kwargs):
... print(f'{username=}, {password=}, {args=}, {kwargs=}')
>>>
>>>
>>> login('alice', 'secret')
username='alice', password='secret', args=(), kwargs={}
>>>
>>> login('alice', 'secret', True, 3600)
username='alice', password='secret', args=(True, 3600), kwargs={}
>>>
>>> login('alice', 'secret', True, timeout=3600)
username='alice', password='secret', args=(True,), kwargs={'timeout': 3600}
>>>
>>> login('alice', 'secret', remember_me=True, timeout=3600)
username='alice', password='secret', args=(), kwargs={'remember_me': True, 'timeout': 3600}
3.3.6. Case Study
Source: https://github.com/django/django/blob/main/django/views/generic/base.py
Out of 20 methods 17 is using either
*args
or**kwargs
(that's 85%)sometimes parameters have different names, such as:
**initkwargs
or**response_kwargs
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.7. 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.8. 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.9. 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.10. 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.11. Assignments
# %% About
# - Name: Star Parameters 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. Define a variadic function `run()`,
# which can takes arbitrary number of positional arguments
# 2. Function does nothing (pass)
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj wariadyczną funkcję `run()`,
# która przyjmuje nieskończoną liczbę pozycyjnych argumentów
# 2. Funkcja nic nie robi (pass)
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `*args`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> run()
>>> run(1)
>>> run(1, 2)
>>> run(1, 2, 3)
>>> run(1, 2, 3, 4)
>>> run(1, 2, 3, 4, 5)
"""
# %% 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
from typing import Callable
run: Callable[[...], None]
# %% Data
# %% Result
def run():
pass
# %% About
# - Name: Star Parameters 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. Define a variadic function `run()`,
# which can takes arbitrary number of keyword arguments
# 2. Function does nothing (pass)
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj wariadyczną funkcję `run()`,
# która przyjmuje nieskończoną liczbę nazwanych argumentów
# 2. Funkcja nic nie robi (pass)
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `*args`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> run()
>>> run(a=1)
>>> run(a=1, b=2)
>>> run(a=1, b=2, c=3)
>>> run(a=1, b=2, c=3, d=4)
>>> run(a=1, b=2, c=3, d=4, e=5)
"""
# %% 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
from typing import Callable
run: Callable[[...], None]
# %% Data
# %% Result
def run():
pass
# %% About
# - Name: Star Parameters ArgsKwargs
# - 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. Define a variadic function `run()`,
# which can takes arbitrary number of positional and keyword arguments
# 2. Function does nothing (pass)
# 3. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj wariadyczną funkcję `run()`,
# która przyjmuje nieskończoną liczbę pozycyjnych i nazwanych argumentów
# 2. Funkcja nic nie robi (pass)
# 3. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `*args`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> run()
>>> run(1)
>>> run(a=1)
>>> run(1, 2)
>>> run(a=1, b=2)
>>> run(1, b=2)
>>> run(1, 2, 3)
>>> run(1, 2, c=3)
>>> run(1, b=2, c=3)
>>> run(a=1, b=2, c=3)
"""
# %% 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
from typing import Callable
run: Callable[[...], None]
# %% Data
# %% Result
def run():
pass