Tracks
/
Python
Python
/
Übungen
/
Lokführer
Lokführer

Lokführer

Lernübung

Einführung

Beim Entpacken ziehst du die Elemente einer Sammlung wie list, tuple oder dict durch Iteration heraus. Die entpackten Werte kannst du dann innerhalb derselben Anweisung Variablen zuweisen. Das nennt man üblicherweise Mehrfachzuweisung.

Die speziellen Operatoren * und ** werden häufig beim Entpacken und bei der Mehrfachzuweisung verwendet.

Caution

*<variable_name> und **<variable_name> darfst du nicht mit * und ** verwechseln. * und ** stehen für die Multiplikation bzw. die Potenzierung, während *<variable_name> und **<variable_name> als Pack- und Entpackoperatoren verwendet werden.

Mehrfachzuweisung

Bei der Mehrfachzuweisung muss die Anzahl der Variablen auf der linken Seite des Zuweisungsoperators (=) mit der Anzahl der Werte auf der rechten Seite übereinstimmen. Trenne die Werte mit einem Komma ,:

>>> a, b = 1, 2
>>> a
1

Bekommt die Mehrfachzuweisung eine falsche Anzahl von Variablen für die angegebenen Werte, wird ein ValueError ausgelöst:

>>> x, y, z = 1, 2

ValueError: too many values to unpack (expected 3, got 2)

Die Mehrfachzuweisung ist nicht auf einen Datentyp beschränkt:

>>> x, y, z = 1, "Hello", True
>>> x
1

>>> y
'Hello'

>>> z
True

Mit der Mehrfachzuweisung kannst du Elemente in lists vertauschen. Das ist in Sortieralgorithmen recht üblich. Zum Beispiel:

>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]

Da tuples unveränderlich sind, kannst du Elemente in einem tuple nicht vertauschen.

Entpacken

Note

Die folgenden Beispiele verwenden lists, aber dieselben Konzepte gelten auch für tuples.

In Python kannst du die Elemente von list/tuple/dictionary in einzelne Variablen entpacken. Da Werte in lists/tuples in einer bestimmten Reihenfolge vorkommen, werden sie in derselben Reihenfolge in Variablen entpackt:

>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"

Wenn du manche Werte nicht brauchst, kannst du sie mit _ markieren:

>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"

Tiefes Entpacken

Das Entpacken und Zuweisen von Werten aus einer list/einem tuple, die in einer list oder einem tuple steckt (auch verschachtelte Listen/Tupel genannt), funktioniert genauso wie ein flaches Entpacken, braucht aber oft Qualifizierer, um den Kontext oder die Position klarzumachen:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"

>>> d
"potato"

Du kannst auch nur einen Teil einer verschachtelten list/eines tuple tief entpacken:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]

>>> c
"carrot"

Wenn beim Entpacken Variablen falsch platziert sind und/oder die Anzahl der Werte nicht stimmt, bekommst du einen ValueError:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [d]] = fruits_vegetables

ValueError: too many values to unpack (expected 1)

Eine Liste/ein Tupel mit * entpacken

Beim Entpacken einer list/eines tuple kannst du mit dem *-Operator die „übrig gebliebenen" Werte auffangen. Das ist übersichtlicher als das Slicing der list/des tuple (was in manchen Situationen weniger lesbar ist). Zum Beispiel kannst du das erste Element herausziehen und die übrigen Werte in eine neue list ohne das erste Element packen:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *last = fruits
>>> x
"apple"

>>> last
["banana", "cherry", "orange", "kiwi", "melon", "mango"]

Wir können auch die Werte am Anfang und am Ende der list herausziehen und dabei alle Werte dazwischen gruppieren:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *middle, y, z = fruits
>>> y
"melon"

>>> middle
["banana", "cherry", "orange", "kiwi"]

Wir können * auch beim tiefen Entpacken verwenden:

>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>>> [[a, *rest], b] = fruits_vegetables
>>> a
"apple"

>>> rest
["banana", "melon"]

Ein Dictionary entpacken

Ein Dictionary zu entpacken unterscheidet sich etwas vom Entpacken einer list/eines tuple. Bei der Iteration über Dictionaries wird standardmäßig über die Schlüssel iteriert. Beim Entpacken eines dict kannst du also nur die Schlüssel entpacken, nicht die Werte:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"

Wenn du die Werte entpacken möchtest, kannst du die Methode <dict>.values() verwenden:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6

Wenn du sowohl Schlüssel als auch Werte brauchst, verwende die Methode <dict>.items(). <dict>.items() erzeugt eine iterierbare Ansicht, die Schlüssel-Wert-Paare enthält. Diese kannst du in ein tuple entpacken:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)

Packen

Beim Packen gruppierst du mehrere Werte in einer list, die einer Variablen zugewiesen wird. Das ist nützlich, wenn du Werte entpacken, ändern und die Ergebnisse anschließend wieder in eine Variable packen möchtest. Außerdem lassen sich damit zwei oder mehr lists/tuples/dicts zusammenführen.

Eine Liste/ein Tupel mit * packen

Das Packen einer list/eines tuple kannst du mit dem *-Operator erledigen. Damit werden alle Werte in eine list/ein tuple gepackt.

>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]

# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>>> combined_fruits = *fruits, *more_fruits

# If there is no * on to the left of the "=" the result is a tuple
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")

# If the * operator is used on the left side of "=" the result is a list.
# Note the trailing comma.
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

Mehr Hintergrund zur Verwendung von * auf der linken Seite findest du in PEP 3132.

Ein Dictionary mit ** packen

Ein Dictionary packst du mit dem **-Operator. Damit packst du alle Schlüssel-Wert-Paare aus einem Dictionary in ein anderes Dictionary oder führst zwei Dictionaries zusammen.

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}

# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

Verwendung von * und ** mit Funktionen

Packen mit Funktionsparametern

Wenn du eine Funktion erstellst, die eine beliebige Anzahl von Argumenten annimmt, kannst du *args oder **kwargs in der Funktionsdefinition verwenden. *args packt eine beliebige Anzahl positionaler (nicht als Schlüsselwort übergebener) Argumente in ein tuple und **kwargs packt eine beliebige Anzahl von Schlüsselwortargumenten in ein Dictionary.

Verwendung von *args:

# This function is defined to take any number of positional arguments

>>> def my_function(*args):
...     print(args)

# Arguments given to the function are packed into a tuple

>>> my_function(1, 2, 3)
(1, 2, 3)

>>> my_function("Hello")
("Hello")

>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")

Verwendung von **kwargs:

# This function is defined to take any number of keyword arguments

>>> def my_function(**kwargs):
...   print(kwargs)

# Arguments given to the function are packed into a dictionary

>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}

*args und **kwargs kannst du auch miteinander kombinieren:

>>> def my_function(*args, **kwargs):
...   print(sum(args))
...   for key, value in kwargs.items():
...       print(str(key) + " = " + str(value))

>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
a = 1
b = 2
c = 3

Du kannst auch Parameter vor *args schreiben, um bestimmte positionale Argumente zuzulassen. Einzelne Schlüsselwortargumente müssen dann vor **kwargs stehen.

Caution

Argumente müssen so strukturiert sein:

def my_function(<positional_args>, *args, <key-word_args>, **kwargs)

Wenn du diese Reihenfolge nicht einhältst, bekommst du einen Fehler.

>>> def my_function(a, b, *args):
...   print(a)
...   print(b)
...   print(args)

>>> my_function(1, 2, 3, 4, 5)
1
2
(3, 4, 5)

Schreibst du die Argumente in der falschen Reihenfolge, führt das zu einem Fehler:

>>>def my_function(*args, a, b):
... print(args)

>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
  File "c:\something.py", line 3, in <module>
    my_function(1, 2, 3, 4, 5)
TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'

Entpacken in Funktionsaufrufe

Mit * kannst du eine list/ein tuple von Argumenten in einen Funktionsaufruf entpacken. Das ist sehr nützlich für Funktionen, die kein iterable akzeptieren:

>>> def my_function(a, b, c):
...   print(c)
...   print(b)
...   print(a)

numbers = [1, 2, 3]
>>> my_function(*numbers)
3
2
1

Das Entpacken mit * zusammen mit dem eingebauten zip() ist ein weiterer häufiger Anwendungsfall. Die Funktion zip() nimmt mehrere Iterables und gibt eine list aus tuples zurück, in der die Werte aus jedem iterable gruppiert sind:

>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]

Anleitung

Dein Freund Linus ist Lokomotivführer und fährt Güterzüge zwischen Städten. Züge zu steuern liegt ihm zwar im Blut, aber mit Logistik und Computern kennt er sich nicht so gut aus. Er würde gerne deine Programmierhilfe in Anspruch nehmen, um Zugdaten zu organisieren und Fehler in den Routendaten zu korrigieren.

Note

Diese Übung ließe sich leicht mit Slicing, Indexing und verschiedenen dict-Methoden lösen. Wir möchten aber, dass du beim Lösen der folgenden Aufgaben das Packen, Entpacken und die Mehrfachzuweisung übst.

1. Erstelle eine Liste aller Waggons

Dein Freund hat bisher jede Waggon-Kennung (ID) mitgeschrieben, aber er weiß nie genau, wie viele Waggons das System zu einem bestimmten Zeitpunkt verarbeiten muss. Für den Rest des Logistikprogramms wäre es viel einfacher, wenn diese Daten in einer einheitlichen list gebündelt vorliegen würden.

Implementiere eine Funktion get_list_of_wagons(), die eine beliebige Anzahl von Waggon-IDs entgegennimmt. Jede ID ist eine positive Ganzzahl. Die Funktion soll die übergebenen IDs dann per return als eine einzelne list zurückgeben.

>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
[1, 7, 12, 3, 14, 8, 5]

2. Korrigiere die Liste der Waggons

Mittlerweile bekommst du ein Gefühl für die Daten und dafür, wie sie im Logistikprogramm verwendet werden. Das ID-System vergibt an die Lokomotive immer die ID 1, während die übrigen Waggons im Zug eine zufällig gewählte ID größer als 1 erhalten.

Dein Freund musste zwei neue Waggons an den Zug anhängen und hat vergessen, das System zu aktualisieren! Jetzt müssen die ersten beiden Waggons in der list des Zuges ans Ende verschoben werden, sonst gerät alles durcheinander.

Erschwerend kommt hinzu, dass dein Freund gerade eine zweite list entdeckt hat, die offenbar fehlende Waggon-IDs enthält. Er kann sich nur noch daran erinnern, dass die IDs aus dieser zweiten list direkt hinter der dafür vorgesehenen Lokomotive platziert werden sollen, sobald die neuen Waggons verschoben sind.

Linus wäre dir sehr dankbar, wenn du seine Fehler behebst und die Daten zusammenführst.

Implementiere eine Funktion fix_list_of_wagons(), die zwei lists mit Waggon-IDs entgegennimmt. Sie soll die ersten beiden Elemente der ersten list ans Ende verschieben und die Werte aus der zweiten list hinter (rechts von) der Lokomotiv-ID (1) einfügen. Die Funktion soll dann eine list mit den Änderungen per return zurückgeben.

>>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15])
[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5]

3. Fehlende Halte hinzufügen

Jetzt, wo alle Waggondaten korrekt sind, möchte Linus, dass du die Routing-Informationen des Systems aktualisierst. Auf einer Transportroute hält ein Zug unter Umständen an ein paar verschiedenen Bahnhöfen, um Fracht aufzunehmen und/oder abzuladen. Jede Fahrt kann eine unterschiedliche Anzahl dieser Zwischenstationen haben. Dein Freund möchte, dass du das Routing-dict des Systems um alle fehlenden/zusätzlichen Lieferinformationen ergänzt.

Implementiere eine Funktion add_missing_stops(), die ein Routing-dict gefolgt von einer variablen Anzahl von Schlüsselwortargumenten entgegennimmt. Diese Argumente können in Form eines dict vorliegen, das einen oder mehrere Halte enthält, oder als beliebig viele stop_number=city-Schlüsselwortpaare. Deine Funktion soll dann das Routing-dict zurückgeben, ergänzt um einen zusätzlichen key, der eine list aller hinzugefügten Halte in der richtigen Reihenfolge enthält.

>>> add_missing_stops({"from": "New York", "to": "Miami"},
                      stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta",
                      stop_4="Jacksonville", stop_5="Orlando")

{"from": "New York", "to": "Miami", "stops": ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]}

4. Routing-Informationen erweitern

Linus hat am Routing-Programm gearbeitet und bemerkt, dass bei bestimmten Routen einige wichtige Details fehlen. Die ursprünglichen Routeninformationen wurden als dict angelegt, und dein Freund möchte, dass du dieses dict um alles ergänzt, was fehlen könnte. Jede Route im System braucht etwas andere Details, deshalb wäre Linus eine generische Lösung wirklich lieber.

Implementiere eine Funktion namens extend_route_information(), die zwei dicts entgegennimmt. Das erste dict enthält die Start- und Zielstädte, zwischen denen die Zugroute verläuft.

Das zweite dict enthält weitere Routing-Details wie Geschwindigkeit, Länge oder Temperatur des Zuges. Die Funktion soll ein zusammengeführtes dict mit allen Routing-Informationen zurückgeben.

Note

Das zweite dict kann andere/mehr Eigenschaften enthalten als die im Beispiel gezeigten.

>>> extend_route_information({"from": "Berlin", "to": "Hamburg"}, {"length": "100", "speed": "50"})
{"from": "Berlin", "to": "Hamburg", "length": "100", "speed": "50"}

5. Das Waggondepot in Ordnung bringen

Als Linus das Waggondepot inspizierte, bemerkte er, dass die Waggons nicht in der richtigen Reihenfolge gelagert wurden. Neben einer ID hat jeder Waggon eine Farbe, die der Art der Fracht entspricht, die er transportiert. Im Depot werden die Waggons in Rastern gelagert, wobei jede Spalte im Raster Waggons derselben Farbe enthält.

Allerdings zeigt das Logistiksystem, dass bei den lists der im Depot zu lagernden Waggons die Zeilen nach Farbe gruppiert sind. Damit das Lagerraster aber korrekt funktioniert, sollte jede Zeile drei verschiedene Farben haben, damit die Spalten nach Farbe ausgerichtet sind. Dein Freund möchte, dass du die lists des Waggondepots sortierst, damit die Waggons korrekt gelagert werden.

Implementiere eine Funktion namens fix_wagon_depot(), die eine list mit drei Elementen entgegennimmt. Jedes Element der list ist eine Unterliste (oder „Zeile“), die drei tuples enthält. Jedes tuple ist ein Paar (<wagon ID>, <wagon color>).

Deine Funktion soll eine list zurückgeben, in der die drei „Zeilen“-lists so umsortiert sind, dass die Waggons an ihren richtigen Positionen stehen.

>>> fix_wagon_depot([
                    [(2, "red"), (4, "red"), (8, "red")],
                    [(5, "blue"), (9, "blue"), (13,"blue")],
                    [(3, "orange"), (7, "orange"), (11, "orange")],
                    ])

[
[(2, "red"), (5, "blue"), (3, "orange")],
[(4, "red"), (9, "blue"), (7, "orange")],
[(8, "red"), (13,"blue"), (11, "orange")]
]
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Python Exercism

Bereit, mit Lokführer zu starten?

Melde dich bei Exercism an, um Python mit 17 Konzepte146 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.