Jak používat operátory rozbalování (*, **) v Pythonu?
Python patří k nejpoužívanějším programovacím jazykům. Dnes si osvojíme jednu z jeho klíčových, avšak často opomíjených funkcí – rozbalování.
Je možné, že jste se s symboly * a ** již setkali v cizím kódu, nebo jste je dokonce sami použili, aniž byste zcela chápali jejich účel. V tomto článku si podrobně vysvětlíme koncept rozbalování a ukážeme si, jak jej využít k psaní ještě elegantnějšího kódu v Pythonu.
Pro lepší pochopení následujícího textu je dobré se seznámit s těmito pojmy:
- Iterovatelný objekt: Jakákoli sekvence, kterou lze procházet pomocí cyklu
for, například seznamy, n-tice, množiny a slovníky. - Volatelný objekt (Callable): Objekt v Pythonu, který lze vyvolat pomocí závorek
(), třeba jakomojefunkce(). - Shell: Interaktivní prostředí pro spouštění kódu Pythonu. Spustíme jej příkazem
pythonv terminálu. - Proměnná: Symbolické jméno, které uchovává odkaz na objekt v paměti.
Začněme s častým nedorozuměním: v Pythonu symboly * mají i funkci aritmetických operátorů. Jedna hvězdička * slouží k násobení, zatímco dvě hvězdičky ** označují umocňování.
>>> 3*3 9 >>> 3**3 27
Ověříme si to spuštěním Python shellu a zadáním příkazů:
Poznámka: Pro pokračování v tomto tutoriálu je nezbytné mít nainstalovaný Python 3. Pokud jej ještě nemáte, podívejte se na návod k instalaci.
Jak vidíte, symbol hvězdičky je umístěn mezi dvěma čísly. V takovém případě se jedná o aritmetické operátory.
>>> *range(1, 6),
(1, 2, 3, 4, 5)
>>> {**{'vanilla':3, 'chocolate':2}, 'strawberry':2}
{'vanilla': 3, 'chocolate': 2, 'strawberry': 2}
Naproti tomu pro rozbalení používáme hvězdičky (*, **) před iterovatelnými objekty, například:
Nezoufejte, pokud tomu zatím zcela nerozumíte, toto je pouze úvod do rozbalování v Pythonu. Pokračujte ve čtení, a vše se vyjasní!
Co vlastně znamená "rozbalovat"?
Rozbalování je proces, při kterém získáváme jednotlivé položky z iterovatelných objektů, jako jsou seznamy, n-tice a slovníky. Představte si to jako otevření krabice a vyjmutí jednotlivých věcí, třeba kabelů, sluchátek nebo USB.
Rozbalování v Pythonu je analogické otevírání krabice v reálném světě.
>>> mybox = ['cables', 'headphones', 'USB'] >>> item1, item2, item3 = mybox
Pro lepší pochopení si tento příklad rozepíšeme v kódu:
Jak je vidět, tři položky ze seznamu mybox přiřazujeme do tří proměnných item1, item2 a item3. Toto je základní koncept rozbalování v Pythonu.
>>> item1 'cables' >>> item2 'headphones' >>> item3 'USB'
Pokud se pokusíme získat hodnotu každé proměnné, zjistíme, že item1 odkazuje na "cables", item2 na "headphones" a tak dále.
>>> newbox = ['cables', 'headphones', 'USB', 'mouse'] >>> item1, item2, item3 = newbox Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: too many values to unpack (expected 3)
Zatím se zdá, že je vše v pořádku. Co se ale stane, pokud budeme chtít rozbalit seznam s více prvky, přičemž počet proměnných zůstane stejný?
Pravděpodobně jste očekávali nějakou chybu. V podstatě se snažíme přiřadit 4 položky seznamu do tří proměnných. Jak by Python měl rozdělit správné hodnoty? Výsledkem je chyba ValueError
s hláškou "příliš mnoho hodnot k rozbalení". Důvodem je, že na levé straně přiřazení máme tři proměnné a na pravé straně čtyři hodnoty (ze seznamu newbox).
>>> lastbox = ['cables', 'headphones'] >>> item1, item2, item3 = lastbox Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: not enough values to unpack (expected 3, got 2)
Pokud se pokusíte o opačnou situaci – tedy rozbalit objekt do více proměnných, než má rozbalovaný objekt hodnot, dostanete opět ValueError, tentokrát s lehce odlišnou hláškou:
Poznámka: V uvedených příkladech jsme pracovali se seznamy, ale tento způsob rozbalování lze použít s jakýmkoli iterovatelným objektem (seznamy, množiny, n-tice, slovníky).
Jak tedy tuto situaci řešit? Existuje nějaký způsob, jak rozbalit všechny položky iterovatelného objektu do menšího počtu proměnných, aniž bychom vyvolali chybu?
Samozřejmě existuje, a používá se k tomu operátor rozbalení, známý také jako operátor s hvězdičkou (*, **). Podívejme se, jak se používá v Pythonu.
Rozbalování seznamů pomocí operátoru *
Operátor hvězdička *
>>> first, *unused, last = [1, 2, 3, 5, 7] >>> first 1 >>> last 7 >>> unused [2, 3, 5]
slouží k rozbalení všech zbývajících hodnot iterovatelného objektu, které ještě nebyly přiřazeny.
>>> first, *_, last = [1, 2, 3, 5, 7] >>> _ [2, 3, 5]
Předpokládejme, že chceme získat první a poslední prvek seznamu bez použití indexů. Můžeme toho docílit použitím operátoru hvězdička:
>>> first, *_, last = [1, 2] >>> first 1 >>> last 2 >>> _ []
Jak je vidět, pomocí operátoru hvězdička získáme všechny nepoužité hodnoty. Pro odhození hodnot se často používá proměnná s názvem podtržítko _, která se někdy nazývá "dummy variable" (fiktivní proměnná).
Tento trik můžeme použít i v případě, kdy seznam obsahuje jen dva prvky:
V tomto případě proměnná podtržítko (fiktivní proměnná) uloží prázdný seznam, takže další dvě proměnné mohou získat dostupné hodnoty ze seznamu.
>>> *string = 'PythonIsTheBest'
Běžné problémy
>>> *string = 'PythonIsTheBest' File "<stdin>", line 1 SyntaxError: starred assignment target must be in a list or tuple
Může se zdát, že můžeme rozbalit jediný prvek iterovatelného objektu. Například: Výše uvedený kód ovšem vyvolá chybu SyntaxError, protože podle
specifikace PEP
>>> *string, = 'PythonIsTheBest' >>> string ['P', 'y', 't', 'h', 'o', 'n', 'I', 's', 'T', 'h', 'e', 'B', 'e', 's', 't']
se přiřazení pomocí hvězdičky (starred assignment) musí vyskytovat uvnitř seznamu nebo n-tice na levé straně rovnítka.
>>> *numbers, = range(5) >>> numbers [0, 1, 2, 3, 4]
Pokud chceme rozbalit všechny hodnoty iterovatelného objektu do jediné proměnné, musíme použít n-tici. Stačí tedy přidat čárku:
Dalším příkladem je použití funkce range, která vrací posloupnost čísel.
Nyní, když už víme, jak rozbalovat seznamy a n-tice pomocí hvězdičky, je čas se zaměřit na rozbalování slovníků.
Rozbalování slovníků pomocí operátoru **
>>> **greetings, = {'hello': 'HELLO', 'bye':'BYE'}
...
SyntaxError: invalid syntax
Zatímco pro rozbalování seznamů a n-tic se používá jedna hvězdička, pro rozbalování slovníků se používají dvě hvězdičky (**).
>>> food = {'fish':3, 'meat':5, 'pasta':9}
>>> colors = {'red': 'intensity', 'yellow':'happiness'}
>>> merged_dict = {**food, **colors}
>>> merged_dict
{'fish': 3, 'meat': 5, 'pasta': 9, 'red': 'intensity', 'yellow': 'happiness'}
Bohužel nelze rozbalit slovník do jedné proměnné, jak jsme to dělali u n-tic a seznamů. Následující tedy povede k chybě:
Operátor ** však můžeme použít uvnitř volatelných objektů (callable) a dalších slovníků. Chceme-li například vytvořit sloučený slovník z několika existujících slovníků, můžeme použít následující kód:
Je to celkem elegantní způsob, jak vytvářet složené slovníky, ale není to hlavní využití rozbalování v Pythonu.
Podívejme se, jak můžeme využít rozbalování s volatelnými objekty.
Balení funkcí: args a kwargs
Pravděpodobně jste se s args a kwargs již setkali při definování tříd nebo funkcí. Podívejme se, proč je musíme používat s volatelnými objekty.
>>> def product(n1, n2): ... return n1 * n2 ... >>> numbers = [12, 1] >>> product(*numbers) 12
Balení s operátorem * (args)
>>> product(12, 1) 12
Představme si, že máme funkci, která vypočítá součin dvou čísel.
>>> numbers = [12, 1, 3, 4] >>> product(*numbers) ... TypeError: product() takes 2 positional arguments but 4 were given
Jak vidíme, rozbalíme prvky seznamu numbers do funkce. Ve skutečnosti tak spouštíme následující kód:
>>> def product(*args): ... result = 1 ... for i in args: ... result *= i ... return result ... >>> product(*numbers) 144
Do této chvíle je vše v pořádku. Co se ale stane, když budeme chtít předat delší seznam? Jistě, dojde k chybě, protože funkce přijímá více argumentů, než umí zpracovat.
Tuto situaci můžeme vyřešit tak, že "zabalíme" seznam přímo do funkce, čímž vytvoříme iterovatelný objekt a umožníme funkci přijímat libovolný počet argumentů.
Zde parametr args zpracováváme jako iterovatelný objekt, procházíme jeho prvky a vracíme součin všech čísel. Všimněte si, že počáteční hodnota proměnné result musí být 1, jinak bychom při startu s nulou vždy získali nulu. Poznámka: args je pouze konvence, můžeme použít jakýkoli název parametru. Také můžeme funkci předávat libovolná čísla bez použití seznamu, stejně jako u vestavěné
>>> product(5, 5, 5) 125 >>> print(5, 5, 5) 5 5 5
funkce print.
>>> def test_type(*args): ... print(type(args)) ... print(args) ... >>> test_type(1, 2, 4, 'a string') <class 'tuple'> (1, 2, 4, 'a string')
.
Nakonec získáme typ objektu args ve funkci.
Jak je vidět ve výše uvedeném kódu, typ args je vždy n-tice a jejím obsahem jsou všechny neklíčové argumenty předané funkci.
Balení s operátorem ** (kwargs)
>>> def make_person(name, **kwargs):
... result = name + ': '
... for key, value in kwargs.items():
... result += f'{key} = {value}, '
... return result
...
>>> make_person('Melissa', id=12112, location='london', net_worth=12000)
'Melissa: id = 12112, location = london, net_worth = 12000, '
Jak už víme, operátor ** se používá výhradně pro slovníky. To znamená, že s tímto operátorem můžeme předávat páry klíč-hodnota jako parametry funkce.
Vytvoříme funkci make_person, která jako první argument přijímá poziční argument name a poté libovolný počet argumentů s klíčovými slovy.
Jak je vidět, **kwargs transformuje všechny argumenty s klíčovými slovy do slovníku, který pak můžeme v těle funkce procházet.
>>> def test_kwargs(**kwargs):
... print(type(kwargs))
... print(kwargs)
...
>>> test_kwargs(random=12, parameters=21)
<class 'dict'>
{'random': 12, 'parameters': 21}
Poznámka: kwargs je pouze konvence, tento parametr můžeme pojmenovat libovolně.
Typ kwargs můžeme zkontrolovat stejným způsobem, jako jsme to udělali s args:
>>> def my_final_function(*args, **kwargs):
... print('Type args: ', type(args))
... print('args: ', args)
... print('Type kwargs: ', type(kwargs))
... print('kwargs: ', kwargs)
...
>>> my_final_function('Python', 'The', 'Best', language="Python", users="A lot")
Type args: <class 'tuple'>
args: ('Python', 'The', 'Best')
Type kwargs: <class 'dict'>
kwargs: {'language': 'Python', 'users': 'A lot'}
Interní proměnná kwargs se vždy transformuje na slovník, který ukládá páry klíč-hodnota předané do funkce.
Nakonec si ukážeme, jak používat args a kwargs v té samé funkci:
Závěr
- Operátory rozbalování jsou velmi užitečné při každodenních úkolech. Nyní už víte, jak je používat jak v samostatných příkazech, tak v parametrech funkcí.
- V tomto článku jste se naučili:
- Používat
*pro n-tice a seznamy a**pro slovníky - Používat operátory rozbalování v konstruktorech funkcí a tříd
args se používají pro předávání neklíčových parametrů do funkcí a kwargs se používají pro předávání parametrů s klíčovými slovy.