2.3. Unpacking Parameters

2.3.1. Recap

  • parameter - Receiving variable used within the function/block

  • required parameters - Parameter which is necessary to call function

  • optional parameters (with default value) - Parameter which is optional and has default value (if not specified at call time)

>>> def echo(a, b=0):
...     print(a)
...     print(b)

2.3.2. Rationale

  • More information in Unpacking Assignment

  • More information in Unpacking Parameters

  • More information in Unpacking Arguments

../../_images/unpacking-assignment,args,params.png

2.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(args)
>>>
>>>
>>> echo()
()
>>>
>>> echo(1)
(1,)
>>>
>>> echo(2, 3)
(2, 3)
>>>
>>> echo('a', 'b')
('a', 'b')
>>>
>>> echo('a', 2, 3.3)
('a', 2, 3.3)
>>> def echo(a, b, c=0, *args):
...     print(f'{a=}, {b=}, {c=}, {args=}')
>>>
>>>
>>> echo(1, 2)
a=1, b=2, c=0, 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)

2.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(kwargs)
>>>
>>>
>>> echo(a=1)
{'a': 1}
>>>
>>> echo(color='red')
{'color': 'red'}
>>>
>>> echo(firstname='Jan', lastname='Twardowski')
{'firstname': 'Jan', 'lastname': 'Twardowski'}
>>> def echo(a, b, c=0, **kwargs):
...     print(f'{a=}, {b=}, {c=}, {kwargs=}')
>>>
>>>
>>> echo(1, 2)
a=1, b=2, c=0, kwargs={}
>>>
>>> echo(1, 2, 3)
a=1, b=2, c=3, kwargs={}
>>>
>>> echo(1, 2, 3, d=7, e=8)
a=1, b=2, c=3, kwargs={'d': 7, 'e': 8}
>>>
>>> echo(1, 2, a=7)
Traceback (most recent call last):
TypeError: echo() got multiple values for argument 'a'

2.3.5. Positional and Keyword Parameters

>>> def echo(a, b, c=0, *args, **kwargs):
...     print(f'{a=}, {b=}, {c=}, {args=}, {kwargs=}')
>>>
>>>
>>> echo(1, 2)
a=1, b=2, c=0, 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)
a=1, b=2, c=3, args=(), kwargs={'d': 7, 'e': 8}
>>>
>>> echo(1, 2, 3, 4, 5, 6, d=7, e=8)
a=1, b=2, c=3, args=(4, 5, 6), kwargs={'d': 7, 'e': 8}

2.3.6. Use Case - Range

>>> def echo(a, b, c=0, *args):
...     print(locals())
>>>
>>> echo(*range(0,10))
{'a': 0, 'b': 1, 'c': 2, 'args': (3, 4, 5, 6, 7, 8, 9)}

2.3.7. Use Case - Sum

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

2.3.8. Use Case - Kelvin to Celsius

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

2.3.9. Use Case - HTML list

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

2.3.10. Use Case - Print

Intuitive definition of print function:

>>> def print(*values, sep=' ', end='\n'):
...     return sep.join(values) + end
>>>
>>>
>>> print('a')
'a\n'
>>>
>>> print('a', 'b')
'a b\n'
>>>
>>> print('a', 'b', 'c')
'a b c\n'
>>>
>>> print('a', 'b', 'c', sep=',')
'a,b,c\n'
>>>
>>> print('a', 'b', 'c', sep='|')
'a|b|c\n'

2.3.11. Assignments

Code 2.23. Solution
"""
* Assignment: Unpacking Parameters Define
* Complexity: easy
* Lines of code: 4 lines
* Time: 5 min

English:
    1. Create function `mean()`, which calculates arithmetic mean
    2. Function can have arbitrary number of positional arguments
    3. Do not import any libraries and modules
    4. Use builtin functions `sum()` and `len()`
    5. Run doctests - all must succeed

Polish:
    1. Napisz funkcję `mean()`, wyliczającą średnią arytmetyczną
    2. Funkcja przyjmuje dowolną ilość pozycyjnych argumentów
    3. Nie importuj żadnych bibliotek i modułów
    4. Użyj wbudowanych funkcji `sum()` i `len()`
    5. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `sum(...) / len(...)`

Tests:
    >>> import sys; sys.tracebacklimit = 0

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


Code 2.24. Solution
"""
* Assignment: Unpacking Parameters Args
* Complexity: easy
* Lines of code: 7 lines
* Time: 8 min

English:
    1. Create function `isnumeric`
    2. Function can have arbitrary number of positional arguments
    3. Arguments can be of any type
    4. Return `True` if all arguments are `int` or `float` only
    5. Return `False` if any argument is different type
    6. Do not use `all()` and `any()`
    7. Run doctests - all must succeed

Polish:
    1. Stwórz funkcję `isnumeric`
    2. Funkcja może przyjmować dowolną liczbę argumentów pozycyjnych
    3. Podawane argumenty mogą być dowolnego typu
    4. Zwróć `True` jeżeli wszystkie argumenty są tylko typów `int` lub `float`
    5. Zwróć `False` jeżeli którykolwiek jest innego typu
    6. Nie używaj `all()` oraz `any()`
    7. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `isinstance(obj, (type1, type2))`
    * `type(obj)`

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isfunction

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

    >>> isnumeric()
    False
    >>> isnumeric(0)
    True
    >>> isnumeric(1)
    True
    >>> isnumeric(-1)
    True
    >>> isnumeric(1.1)
    True
    >>> isnumeric('one')
    False
    >>> isnumeric([1, 1.1])
    False
    >>> isnumeric(1, 1.1)
    True
    >>> isnumeric(1, 'one')
    False
    >>> isnumeric(1, 'one', 'two')
    False
    >>> isnumeric(True)
    False
"""


Code 2.25. Solution
"""
* Assignment: Unpacking Parameters Kwargs
* Complexity: medium
* Lines of code: 8 lines
* Time: 8 min

English:
    1. Create function `isnumeric`
    2. Function can have arbitrary number of positional **and keyword arguments**
    3. Arguments can be of any type
    4. Return `True` if all arguments are `int` or `float` only
    5. Return `False` if any argument is different type
    6. Do not use `all()` and `any()`
    7. Compare using `type()` and `isinstance()` passing `True` as an argument
    8. Run the function without any arguments
    9. Run doctests - all must succeed

Polish:
    1. Stwórz funkcję `isnumeric`
    2. Funkcja może przyjmować dowolną liczbę argumentów pozycyjnych **i nazwanych**
    3. Podawane argumenty mogą być dowolnego typu
    4. Zwróć `True` jeżeli wszystkie argumenty są tylko typów `int` lub `float`
    5. Zwróć `False` jeżeli którykolwiek jest innego typu
    6. Nie używaj `all()` oraz `any()`
    7. Porównaj użycie `type()` i `isinstance()` podając argument do funkcji `True`
    8. Uruchom funkcję bez podawania argumentów
    9. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `isinstance(obj, (type1, type2))`
    * `type(obj)`

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isfunction

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

    >>> isnumeric()
    False
    >>> isnumeric(0)
    True
    >>> isnumeric(1)
    True
    >>> isnumeric(-1)
    True
    >>> isnumeric(1.1)
    True
    >>> isnumeric('one')
    False
    >>> isnumeric([1, 1.1])
    False
    >>> isnumeric(1, 1.1)
    True
    >>> isnumeric(1, 'one')
    False
    >>> isnumeric(1, 'one', 'two')
    False
    >>> isnumeric(True)
    False
    >>> isnumeric(a=1)
    True
    >>> isnumeric(a=1.1)
    True
    >>> isnumeric(a='one')
    False
"""