2022-11-19 09:19 Doba čtení: 16 min

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 jako mojefunkce().
  • Shell: Interaktivní prostředí pro spouštění kódu Pythonu. Spustíme jej příkazem python v 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.

Petra Kovářová
Autor
Czechia

Sleduje mobilní technologie, Android/iOS a praktické návody pro uživatele.

Předchozí článek
Pochopení Java vs JavaScript
Další článek
Jak nainstalovat Google Chrome na Ubuntu