3. Passing many arguments

3.1. Operators * i **

  • This is not multiplication or power!
  • *args - positional arguments
  • **kwargs - keyword arguments
  • *args unpacks tuple or list
  • **kwargs unpacks to dict

3.2. * unpacks list or tuple

complex(3, 5)
args = (3, 5)
complex(*args)

3.3. ** unpacks dict

complex(real=3, imag=5)
kwargs = {'real': 3, 'imag': 5}
complex(**number)

3.4. Przekazywanie do funkcji zmiennej ilości parametrów

Przykładowe zastosownaie operatorów * i ** polega na wykorzystaniu ich przy wywołaniu funkcji. Wtedy, wykorzystując operator *, kolejne elementy listy albo krotki będą przekazane jako kolejne argumenty funkcji, a wykorzystując operator ** kolejne elementy zmiennej słownikowej będą przekazane jako nazwane argumenty. Oznacza to, że na przykład argument x funkcji, przyjmie wartość vector['x'].

def my_function(x, y, z):
    print(x, y, z)

vector = (1, 0, 1)
my_function(*vector)   # my_function(1, 0, 1)
# 1, 0, 1

vector = {'y': 1, 'x': 0, 'z': 1}
my_function(**vector)  # my_function(y=1, x=0, z=1)
# 0, 1, 1
def show(a, b, c=0):
    print(locals())

show(1, 2, 3)
# {'a': 1, 'b': 2, 'c': 3}

dane = (1, 2, 3)
show(*dane)
# {'a': 1, 'b': 2, 'c': 3}

dane = (1, 2)
show(*dane)
# {'a': 1, 'b': 2, 'c': 0}
def show(a, b, c=0, *args):
    print(locals())

dane = (1, 2, 3, 4)
show(*dane)
# {'a': 1, 'b': 2, 'c': 3, 'args': (4,)}

dane = (1, 2, 3, 4, 5, 6, 7)
show(*dane)
# {'a': 1, 'b': 2, 'c': 3, 'args': (4, 5, 6, 7)}

show(1, 2)
# {'a': 1, 'b': 2, 'c': 0, 'args': ()}
def show(a, b, c=0, *args, **kwargs):
    print(locals())

show(1, 2, x=77, y=99)
# {'a': 1, 'b': 2, 'c': 0, 'args': (), 'kwargs': {'x': 77, 'y': 99}}

show(1, 2, x=77, y=99, c=7)
# {'a': 1, 'b': 2, 'c': 7, 'args': (), 'kwargs': {'x': 77, 'y': 99}}

dane = {'x': 77, 'y': 99}
show(1, 2, 3, **dane)
# {'a': 1, 'b': 2, 'c': 3, 'args': (), 'kwargs': {'x': 77, 'y': 99}}

dane = {'a': 1, 'b': 2, 'x': 77, 'y': 99}
show(**dane)
# {'a': 1, 'b': 2, 'c': 0, 'args': (), 'kwargs': {'x': 77, 'y': 99}}
def show(a, b, c=0, *args, **kwargs):
    print(locals())

dane = {'x': 77, 'y': 99, 'a': 7}
show(1, 2, 3, **dane)
# TypeError: show() got multiple values for argument 'a'
def show(a, b, c=0, *args, **kwargs):
    print(locals())

show(1, 2, 3, 4, 5, 6, x=77, y=99)
# {'a': 1, 'b': 2, 'c': 3, 'args': (4, 5, 6), 'kwargs': {'x': 77, 'y': 99}}

pozycyjne = (4, 5, 6)
nazwane = {'x': 77, 'y': 99}
show(1, 2, 3, *pozycyjne, **nazwane)
# {'a': 1, 'b': 2, 'c': 3, 'args': (4, 5, 6), 'kwargs': {'x': 77, 'y': 99}}

3.5. Przykładowe zastosowanie

3.5.1. Konwersja Temperatury

from typing import List

def celsius_to_fahrenheit(*degrees) -> List[float]:
    return [x * 1.8 + 32 for x in degrees]


celsius_to_fahrenheit(1)
# [33.8]

celsius_to_fahrenheit(1, 2, 3, 4, 5)
# [33.8, 35.6, 37.4, 39.2, 41.0]

3.5.2. Podawanie parametrów do funkcji

def draw_line(x, y, color, style, width, markers):
    ...


draw_line(1, 2, color='red', style='dashed', width='2px', markers='disc')
draw_line(3, 4, color='red', style='dashed', width='2px', markers='disc')
draw_line(5, 6, color='red', style='dashed', width='2px', markers='disc')
def draw_chart(a, b, color, style, width, markers):
    ...


config = {
    'color': 'czerwony',
    'style': 'dashed',
    'width': '2px',
    'markers': 'disc',
}

draw_line(1, 2, **config)
draw_line(3, 4, **config)
draw_line(5, 6, **config)

3.5.3. Placeholder class

DATA = [
    {"sepal_length": 6.0, "sepal_width": 3.4, "petal_length": 4.5, "petal_width": 1.6, "species": "versicolor"},
    {"sepal_length": 4.9, "sepal_width": 3.1, "petal_length": 1.5, "petal_width": 0.1, "species": "setosa"},
]

class Iris:
    def __init__(self, sepal_length, sepal_width, petal_length, petal_width, species):
        self.sepal_length = sepal_length
        self.sepal_width = sepal_width
        self.petal_length = petal_length
        self.petal_width = petal_width
        self.species = species

flowers = []

for row in DATA:
    flower = Iris(**row)
    flowers.append(flower)
class Kontakt:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)


Kontakt(imie='Jan', nazwisko='Twardowski')
Kontakt(sepal_length=6.0, sepal_width=3.4, nazwisko='Twardowski')


DATA = {"sepal_length": 6.0, "sepal_width": 3.4, "petal_length": 4.5, "petal_width": 1.6, "species": "versicolor"},
Kontakt(**DATA)


DATA = [
    {"sepal_length": 6.0, "sepal_width": 3.4, "petal_length": 4.5, "petal_width": 1.6, "species": "versicolor"},
    {"sepal_length": 4.9, "sepal_width": 3.1, "petal_length": 1.5, "petal_width": 0.1, "species": "setosa"},
]
Kontakt(**DATA)

3.5.5. Calling function with all variables from higher order function

def show(*args, **kwargs):
    print(f'args: {args}')
    print(f'kwargs: {kwargs}')

def function(a, b, c=0):
    x = 4
    y = 5

    show(**locals())

function(1, 2)
# args: ()
# kwargs: {'a': 1, 'b': 2, 'c': 0, 'x': 4, 'y': 5}

3.6. Assignments

3.6.1. Iris

  1. Otwórz link w przeglądarce i skopiuj zawartość do pliku na dysku o nazwie iris.csv

  2. Stwórz funkcję print_iris(sepal_length, sepal_width, *args, **kwargs), która wyświetli zawartość wszystkich argumentów za pomocą locals()

  3. Sparsuj zawartość pliku iris.csv odrzucając nagłówek

  4. Dla każdego rekordu:

    1. Usuń białe spacje

    2. Podziel po przecinku ,

    3. Wyniki podziału zapisz do dwóch zmiennych:

      • features: List[float] - pomiary
      • labels: Dict[str, str] - key: słowo species, value: nazwa gatunku
    4. Odpalaj funkcję print_iris(), podając wartości features i labels

    5. Pomiary mają być podane pozycyjnie (*), a gatunek nazwanie (**)