Wie verwende ich die Entpackoperatoren (*, **) in Python?

Python, eine der meistgenutzten Programmiersprachen, birgt viele Geheimnisse. Heute widmen wir uns einer ihrer zentralen, aber oft übersehenen Funktionen: dem Entpacken.

Vielleicht sind Ihnen die Symbole * und ** im Code begegnet, ohne deren Bedeutung vollends zu erfassen. Wir beleuchten das Konzept des Entpackens und zeigen, wie es Ihren Python-Code lesbarer und effizienter macht.

Einige Grundbegriffe sind für das Verständnis dieses Tutorials hilfreich:

  • Iterierbar: Jede Sequenz, die sich mit einer for-Schleife durchlaufen lässt, wie Listen, Mengen, Tupel und Dictionaries.
  • Callable: Ein Python-Objekt, das mit runden Klammern () aufgerufen werden kann, z. B. eine Funktion meine_funktion().
  • Shell: Eine interaktive Ausführungsumgebung, in der wir Python-Code direkt testen können. Sie wird durch Eingabe von python im Terminal gestartet.
  • Variable: Ein symbolischer Name, der einen Speicherplatz für ein Objekt reserviert.

Ein häufiger Stolperstein: Das Sternchen * dient in Python auch als arithmetischer Operator. Ein einzelnes Sternchen steht für die Multiplikation, während zwei (**) die Potenzierung repräsentieren.

>>> 3*3
9
>>> 3**3
27

Um dies zu verifizieren, starten Sie eine Python-Shell und geben Sie obigen Code ein.

Hinweis: Sie benötigen Python 3, um diesem Tutorial folgen zu können. Falls Sie es nicht installiert haben, finden Sie Anleitungen zur Python-Installation.

Wie Sie sehen, wird das Sternchen zwischen zwei Zahlen als arithmetischer Operator verwendet.

>>> *range(1, 6),
(1, 2, 3, 4, 5)
>>> {**{'vanille':3, 'schoko':2}, 'erdbeere':2}
{'vanille': 3, 'schoko': 2, 'erdbeere': 2}

Im Gegensatz dazu verwenden wir * und ** vor einem iterierbaren Objekt (z. B. einer Liste), um dessen Elemente zu „entpacken“.

Keine Sorge, falls das noch nicht ganz klar ist. Dies ist nur eine Einführung in das Entpacken. Lesen Sie einfach weiter!

Was bedeutet Entpacken?

Entpacken ist der Prozess, bei dem man einzelne Elemente aus iterierbaren Objekten wie Listen, Tupel oder Dictionaries extrahiert. Stellen Sie sich eine Kiste vor, die Sie öffnen, um verschiedene Gegenstände wie Kabel, Kopfhörer oder USB-Sticks herauszuholen.

Das Entpacken in Python ist analog zum Öffnen einer solchen Kiste.

>>> meine_kiste = ['kabel', 'kopfhörer', 'USB']
>>> gegenstand1, gegenstand2, gegenstand3 = meine_kiste

Verdeutlichen wir dies an einem einfachen Beispiel:

Hier werden die drei Elemente der Liste meine_kiste den Variablen gegenstand1, gegenstand2 und gegenstand3 zugewiesen. Diese Form der Variablenzuweisung ist das Kernkonzept des Entpackens in Python.

>>> gegenstand1
'kabel'
>>> gegenstand2
'kopfhörer'
>>> gegenstand3
'USB'

Wenn wir die einzelnen Variablen abfragen, stellen wir fest, dass sie die entsprechenden Werte aus der Liste enthalten.

>>> neue_kiste = ['kabel', 'kopfhörer', 'USB', 'maus']
>>> gegenstand1, gegenstand2, gegenstand3 = neue_kiste
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)

Soweit so gut. Was passiert aber, wenn wir versuchen, eine Liste mit mehr Elementen in weniger Variablen zu entpacken?

Wie zu erwarten, erhalten wir eine Fehlermeldung. Wir versuchen, vier Listenelemente drei Variablen zuzuweisen. Wie soll Python die Werte korrekt zuordnen? Das ist nicht möglich, daher erhalten wir einen ValueError.

Die Meldung „too many values to unpack“ weist darauf hin, dass links drei Variablen und rechts vier Werte stehen (entsprechend der Liste neue_kiste).

>>> letzte_kiste = ['kabel', 'kopfhörer']
>>> gegenstand1, gegenstand2, gegenstand3 = letzte_kiste
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)

Ein ähnliches Problem tritt auf, wenn wir versuchen, mit mehr Variablen als Elementen zu entpacken. Auch dann gibt es einen ValueError mit einer leicht abgeänderten Fehlermeldung.

Hinweis: Wir haben hier Listen verwendet, aber diese Form des Entpackens funktioniert mit allen iterierbaren Objekten (Listen, Mengen, Tupel, Dictionaries).

Wie können wir dieses Problem lösen? Gibt es eine Möglichkeit, alle Elemente eines iterierbaren Objekts in eine geringere Anzahl von Variablen zu entpacken, ohne Fehler zu erhalten?

Die Antwort lautet: ja! Der sogenannte Entpack- oder Sternchenoperator (*, **) macht’s möglich. Sehen wir uns seine Anwendung in Python an.

Listen mit dem Operator * entpacken

Der Sternchenoperator

>>> erste, *unbenutzt, letzte = [1, 2, 3, 5, 7]
>>> erste
1
>>> letzte
7
>>> unbenutzt
[2, 3, 5]

extrahiert alle nicht zugewiesenen Werte eines iterierbaren Objekts.

>>> erste, *_, letzte = [1, 2, 3, 5, 7]
>>> _
[2, 3, 5]

Angenommen, wir wollen das erste und das letzte Element einer Liste extrahieren, ohne auf Indizes zurückzugreifen. Dies erreichen wir mit dem Sternchenoperator:

>>> erste, *_, letzte = [1, 2]
>>> erste
1
>>> letzte
2
>>> _
[]

Der Sternchenoperator „sammelt“ alle nicht verwendeten Werte. Konventionsgemäß werden überflüssige Werte einer Unterstrichvariable (_) zugewiesen, die oft als „Dummy-Variable“ verwendet wird.

Dieser Trick funktioniert auch, wenn die Liste nur zwei Elemente enthält:

In diesem Fall ist die Unterstrichvariable (Dummy-Variable) eine leere Liste, während die anderen Variablen die verfügbaren Werte erhalten.

>>> *string = 'PythonIstDasBeste'

Häufige Fehlerbehebung

>>> *string = 'PythonIstDasBeste'
  File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple

Wir könnten geneigt sein, ein einzelnes Element eines iterierbaren Objekts zu entpacken. Beispielsweise in der folgenden Form: Der obige Code führt aber zu einem SyntaxError, denn laut PEP-Spezifikation

Die

PEP-Spezifikation

schreibt vor:

>>> *string, = 'PythonIstDasBeste'
>>> string
['P', 'y', 't', 'h', 'o', 'n', 'I', 's', 't', 'D', 'a', 's', 'B', 'e', 's', 't']

Es muss ein Tupel (oder eine Liste) auf der linken Seite der Zuweisung stehen.

>>> *zahlen, = range(5)
>>> zahlen
[0, 1, 2, 3, 4]

Um alle Werte eines iterierbaren Objekts in einer einzigen Variablen zu speichern, müssen wir ein Tupel deklarieren. Ein einfaches Komma reicht also aus.

Ein weiteres Beispiel ist die Verwendung der Funktion range(), die eine Zahlenfolge generiert.

Nachdem wir das Entpacken von Listen und Tupeln mit dem einfachen Sternchen verstanden haben, wenden wir uns nun dem Entpacken von Dictionaries zu.

Dictionaries mit dem Operator ** entpacken

>>> **grüsse, = {'hallo': 'HALLO', 'tschüss':'TSCHÜSS'} 
...
SyntaxError: invalid syntax

Während ein einzelnes Sternchen zum Entpacken von Listen und Tupeln dient, kommt das doppelte Sternchen (**) beim Entpacken von Dictionaries zum Einsatz.

>>> essen = {'fisch':3, 'fleisch':5, 'pasta':9} 
>>> farben = {'rot': 'intensität', 'gelb':'fröhlichkeit'}
>>> vereinigtes_dict = {**essen, **farben}
>>> vereinigtes_dict
{'fisch': 3, 'fleisch': 5, 'pasta': 9, 'rot': 'intensität', 'gelb': 'fröhlichkeit'}

Ein Dictionary lässt sich nicht wie Listen oder Tupel in eine einzelne Variable entpacken. Folgender Code erzeugt einen Fehler:

Wir können den Operator ** jedoch in Funktionen und in anderen Dictionaries verwenden. Um ein kombiniertes Dictionary aus mehreren Dictionaries zu erstellen, können wir folgenden Code nutzen:

Diese kompakte Methode zur Erstellung von zusammengefügten Dictionaries ist nicht die Hauptanwendung des Entpackens in Python.

Sehen wir uns die Verwendung des Entpackens in Funktionen an.

Entpacken in Funktionen: args und kwargs

Vielleicht sind Ihnen die Ausdrücke *args und **kwargs in Funktionen oder Klassen begegnet. Wir erklären ihre Anwendung.

>>> def produkt(n1, n2):
...     return n1 * n2
... 
>>> zahlen = [12, 1]
>>> produkt(*zahlen)
12

Entpacken mit dem Operator * (args)

>>> produkt(12, 1)
12

Angenommen, wir haben eine Funktion, die das Produkt zweier Zahlen berechnet.

>>> zahlen = [12, 1, 3, 4]
>>> produkt(*zahlen)
...
TypeError: product() takes 2 positional arguments but 4 were given

Wir „entpacken“ die Liste zahlen in die Funktion. Tatsächlich führen wir Folgendes aus:

>>> def produkt(*args):
...     resultat = 1
...     for i in args:
...             resultat *= i
...     return resultat
...
>>> produkt(*zahlen)
144

Bis hierhin funktioniert alles. Was aber, wenn wir eine längere Liste übergeben? Es kommt zu einem Fehler, da die Funktion mehr Argumente erhält, als sie verarbeiten kann.

Die Lösung: Wir können die Liste direkt in die Funktion „packen“. Dies erzeugt ein iterierbares Objekt und ermöglicht es uns, eine beliebige Anzahl von Argumenten zu übergeben.

Hier wird der Parameter args als iterierbares Objekt behandelt. Wir durchlaufen die Elemente und geben das Produkt aller Zahlen zurück. Beachten Sie, dass der Startwert des Produkts eins sein muss, da eine Startzahl Null das Ergebnis immer auf Null setzen würde. Hinweis: args ist nur eine Namenskonvention. Sie können jeden anderen Parameternamen verwenden. Wir können auch beliebige Zahlen an die Funktion übergeben, ohne eine Liste zu nutzen, wie bei der integrierten

>>> produkt(5, 5, 5)
125
>>> print(5, 5, 5)
5 5 5

Ausgabefunktion.

>>> def test_typ(*args):
...     print(type(args))
...     print(args)
... 
>>> test_typ(1, 2, 4, 'ein string')
<class 'tuple'>
(1, 2, 4, 'ein string')

Ermitteln wir abschließend den Objekttyp der Argumente in einer Funktion.

Wie der Code zeigt, ist der Typ der args immer ein Tupel. Die Elemente sind alle Nicht-Schlüsselwort-Argumente, die an die Funktion übergeben werden.

Entpacken mit dem Operator ** (kwargs)

>>> def person_erstellen(name, **kwargs):
...     resultat = name + ': '
...     for key, value in kwargs.items():
...             resultat += f'{key} = {value}, '
...     return resultat
... 
>>> person_erstellen('Melissa', id=12112, ort='london', vermögen=12000)
'Melissa: id = 12112, ort = london, vermögen = 12000, '

Wie bereits erwähnt, ist der Operator ** exklusiv für Dictionaries reserviert. Mit diesem Operator können wir Schlüssel-Wert-Paare als Parameter an die Funktion übergeben.

Nehmen wir eine Funktion person_erstellen, die ein Positionsargument (name) und eine unbestimmte Anzahl von Schlüsselwortargumenten erhält.

Die Anweisung **kwargs wandelt alle Schlüsselwortargumente in ein Dictionary um, durch das wir innerhalb der Funktion iterieren können.

>>> def test_kwargs(**kwargs):
...     print(type(kwargs))
...     print(kwargs)
... 
>>> test_kwargs(zufall=12, parameter=21)
<class 'dict'>
{'zufall': 12, 'parameter': 21}

Hinweis: kwargs ist eine Konvention. Sie können auch jeden anderen Parameternamen wählen.

Wir können den Typ von kwargs analog zu args überprüfen:

>>> def meine_endgültige_funktion(*args, **kwargs):
...     print('Typ args: ', type(args))
...     print('args: ', args)
...     print('Typ kwargs: ', type(kwargs))
...     print('kwargs: ', kwargs)
... 
>>> meine_endgültige_funktion('Python', 'Das', 'Beste', sprache="Python", nutzer="Viele")
Typ args:  <class 'tuple'>
args:  ('Python', 'Das', 'Beste')
Typ kwargs:  <class 'dict'>
kwargs:  {'sprache': 'Python', 'nutzer': 'Viele'}

Die interne Variable kwargs wird immer in ein Dictionary konvertiert, welches die übergebenen Schlüssel-Wert-Paare enthält.

Verwenden wir abschließend args und kwargs in einer einzigen Funktion:

Fazit

  • Entpackoperatoren sind sehr nützlich. Sie wissen jetzt, wie Sie sie sowohl in einzelnen Anweisungen als auch bei Funktionsparametern einsetzen können.
  • In diesem Tutorial haben Sie Folgendes gelernt:
  • * wird für Tupel und Listen, ** für Dictionaries verwendet.
  • Sie können Entpackoperatoren in Funktionen und Klassenkonstruktoren nutzen.

*args werden für Parameter verwendet, die keine Schlüsselwörter sind, während kwargs für Schlüsselwortparameter verwendet werden.