2.2. Unpacking Parameters

2.2.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.2.2. Rationale

  • More information in Unpacking Assignment

  • More information in Unpacking Parameters

  • More information in Unpacking Arguments

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

2.2.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.2.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.2.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.2.6. Examples

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

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]

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

Intuitive definition of print function:

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


print('a')
# a

print('a', 'b')
# 'a b'

print('a', 'b', 'c')
# 'a b c'

print('a', 'b', 'c', sep=',')
# 'a,b,c'

print('a', 'b', 'c', sep='|')
# 'a|b|c'

2.2.7. Assignments

Code 2.4. 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.5. 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.6. 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
"""