2Schnelleinstieg

2.1Hallo Welt (Hello World)

Wir beginnen das Kennenlernen von Python mit einem simplen Programm, nämlich wie traditionell in den allermeisten Büchern zum Programmierenlernen mit »Hello World«, der Ausgabe eines Grußes auf der Konsole. Das haben wir schon in etwas komplizierterer Form in der Einleitung gesehen. Wir wollen es aber weiter vereinfachen und damit unsere Entdeckungsreise starten.

Python bietet einen interaktiven Kommandozeileninterpreter, auch Read-Eval-Print-Loop (REPL) genannt. Dieser interaktive Kommandozeileninterpreter erlaubt es uns, kleinere Python-Codeschnipsel auszuprobieren. Das erleichtert das schrittweise Erlernen.

Wir starten den interaktiven Kommandozeileninterpreter mit dem Kommando python (Windows) bzw. python3 (macOS) wie folgt:

$ python3

Python 3.11.2 (v3.11.2:878ead1ac1, Feb 7 2023, 10:02:41) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin

Type "help", "copyright", "credits" or "license" for more information.

>>>

Neben dem Hinweis auf die Version zeigt der Prompt >>> an, dass wir nun Python-Befehle eingeben können. Probieren wir es gleich einmal aus:

>>> print("Hello World from Python!")

Hello World from Python!

Herzlichen Glückwunsch zur ersten Programmausgabe mit Python. Die eingebaute Funktionalität print() ermöglicht Ausgaben auf der Konsole. Derartige Aktionen werden durch sogenannte Funktionen bereitgestellt, in diesem Fall durch die Funktion print(), der man in runden Klammern einen Text eingerahmt von Anführungszeichen übergibt.

Selbst wenn es in diesem Schnelleinstieg-Kapitel ein klein wenig anspruchsvoller wird, keine Sorge, Sie müssen noch nicht alle Details verstehen – wir werden diese im Verlauf dieses Buchs noch ausführlich besprechen. Jetzt geht es zunächst um die ersten Schritte und ein initiales Gespür. Tippen Sie die Beispiele einfach ab und wenn Sie sich sicher fühlen, dann variieren Sie diese auch gerne ein wenig.

Wir haben bereits einen netten Gruß auf der Konsole ausgeben können. Das war schon ein recht guter Anfang. Allerdings wäre es etwas eintönig, nur Texte ohne Variation auszugeben. Um den Gruß anzupassen oder Berechnungen ausführen zu können, lernen wir nun Variablen kennen.

Übrigens hätte man die Ausgabe sogar noch etwas kürzer erhalten können – allerdings ohne die wichtige Funktion print() kennenzulernen:

>>> "Hello World from Python!"

'Hello World from Python!'

2.2Variablen und Datentypen

Stellen wir uns vor, wir wollten einige Eigenschaften einer Person mit Python verwalten, etwa Name, Alter, Gewicht, Wohnort, Familienstand usw. Dabei helfen uns Variablen. Diese dienen zum Speichern und Verwalten von Werten. Dabei gibt es unterschiedliche Typen (auch Datentypen genannt), etwa für Texte, Zahlen und Wahrheitswerte. Wir schauen uns dies zunächst einmal einführend an und erweitern dann unser Wissen Schritt für Schritt, beispielsweise um ergänzende Infos oder Aktionen mit den Variablen. Zum Einstieg betrachten und nutzen wir folgende Typen:

2.2.1Definition von Variablen

In Python erzeugen wir eine Variable, indem wir zunächst den Namen und danach eine Wertzuweisung nach folgendem Muster (auch Syntax genannt) nutzen:

variablenname = wert

Schauen wir uns ein paar Beispiele an:

>>> age = 18

>>> the_answer = 41

>>> simple_pi = 3.1415

>>> name = "Michael"

>>> one_million = 1_000_000

>>> the_answer = 42

>>> is_valid = True

Für das Beispiel mit einer Million sehen wir die Angabe von Unterstrichen, die sich gut zur Separierung, hier von Tausendersegmenten, eignen. Zwar habe ich es für the_answer gerade nur angedeutet, aber einer Variablen kann im Programmverlauf mehrmals ein anderer Wert zugewiesen werden. Denken Sie etwa an einen Punktestand in einem Spiel, einen Temperaturverlauf pro Tag und eine Variable temperature oder aber die Modellierung der Tageszeit von Morgens, Mittags, Nachmittags, Abends und Nachts. Für derart in sich abgeschlossene Wertebereiche existieren Aufzählungen, die wir in Abschnitt 6.5 kennenlernen werden.

Den aktuellen Wert einer Variablen können wir durch Eingabe des Namens in der Konsole abfragen bzw. auslesen.

>>> age

18

>>> name

'Michael'

>>> one_million

1000000

Alternativ können wir die bereits kurz vorgestellte Funktion print() nutzen, die einzelne Werte ausgibt:

>>> print(age)

18

>>> print(name)

Michael

>>> print(one_million)

1000000

Wir erkennen in beiden Fällen, dass die Simulation der Tausendertrennzeichen nur bei der Definition zum Tragen kommt, Python die Werte jedoch ohne diese Zeichen speichert.

2.2.2Variablen und Typen

Tatsächlich sind die Typen von Variablen in Python nicht direkt sichtbar, aber sie lassen sich mit der eingebauten Funktion type() folgendermaßen abfragen:

>>> type(age)

<class 'int'>

>>> type(simple_pi)

<class 'float'>

>>> type(name)

<class 'str'>

>>> type(is_valid)

<class 'bool'>

Neben den kurz erwähnten Typnamen sehen wir weitere Informationen wie class, die hier nicht relevant sind und erst später in Kapitel 4 besprochen werden.

Dynamische Typisierung und Typwechsel

Python ist eine sogenannte dynamisch typisierte Sprache. Das bedeutet, dass sich zum einen der Typ ausgehend vom Wert ergibt und sich der Typ zum anderen sogar im Programmverlauf ändern kann, etwa von float

>>> age = 50.25

>>> type(age)

<class 'float'>

auf str durch eine entsprechende Zuweisung eines textuellen Werts:

>>> age = "Mittelalt"

>>> type(age)

<class 'str'>

Allerdings ist eine derartige Wiederverwendung kein guter Stil und kann zu Missverständnissen und Fehlverwendungen führen. Das gilt insbesondere, wenn im zeitlichen Verlauf statt einer Zahl ein Text gespeichert wird und somit der Typ wechselt.

Typumwandlung (Cast)

Gelegentlich sind Ganzzahlen statt Gleitkommazahlen als Ergebnis gewünscht. Liefert eine Berechnung jedoch Gleitkommazahlen, so können wir mit dem sogenannten Casting arbeiten: Durch die Angabe typ(wert), wobei typ für str, int usw. steht, lässt sich ein Wert in den angegebenen Typ umwandeln. Nachfolgend nutzen wir int, um eine Gleitkommazahl in eine Ganzzahl zu verwandeln, wodurch die Nachkommastellen wie gewünscht abgeschnitten werden. Somit wird aus 3.1415 der Wert 3:

>>> int(3.1415)

3

Bei diesen Casts ändert sich insbesondere der Typ. In unserem Beispiel war das Abschneiden der Nachkommastellen ein schöner und gewünschter Nebeneffekt.

2.2.3Ausgaben mit print()

Schauen wir uns nun noch ein paar Details im Zusammenhang mit der Funktion print() an, die einzelne Werte ausgibt – mehrere textuelle Werte kann man mit + verknüpfen und mit upper() in Großbuchstaben wandeln:

>>> print(age)

18

>>> print("Hello " + name.upper())

Hello MICHAEL

>>>

Auf ein Detail möchte ich nochmals hinweisen: Bei print() handelt es sich um eine in Python integrierte sogenannte Funktion, man spricht auch von Built-in-Funktion. Diese dienen dazu, gewisse Aktionen standardisiert bereitstellen zu können. Funktionen schauen wir uns dann in Abschnitt 2.5 genauer an.

Besonderheit: Zahlen und Texte gemischtBislang scheint die Ausgabe mit print() recht eingängig. Was passiert aber, wenn wir Zahlen und Texte gemischt ausgeben wollen? Das wäre etwa der Fall, wenn wir die meisten der bisher definierten Variablen in eine Konsolenausgabe integrieren wollen.

Betrachten wir zunächst ein vereinfachtes Beispiel bei dem wir – wie möglicherweise von anderen Programmiersprachen gewohnt – die auszugebenden Bestandteile mit + verbinden:

>>> anzahl = 3

>>> print("Bitte " + anzahl + " Mal klingeln!")

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: can only concatenate str (not "int") to str

Das führt zu einem Programmabbruch. Wir erkennen hier, dass nur textuelle Bestandteile mit + verknüpft werden können – ein simpler Cast mit str() löst das Problem:

>>> print("Bitte " + str(anzahl) + " Mal klingeln!")

Bitte 3 Mal klingeln!

Weil die Verknüpfung von textuellen Bestandteilen zur Ausgabe recht gebräuchlich ist, ist eine Alternative in Python integriert: An print() kann man eine kommaseparierte Folge von Werten übergeben, die dann automatisch in Strings umgewandelt und mit einem Abstand von einem Leerzeichen ausgegeben werden:

>>> print("Hello", name, "the answer is", the_answer, "and not", age)

Hello Michael the answer is 42 and not 18

Aber Moment: Wo sind denn die anfangs erwähnten Typen? Darauf kommen wir zurück, nachdem wir uns die Konsolenausgabe etwas genauer angeschaut haben.

Besonderheit: SeparatorWenn die Werte nicht mit einem Abstand von einem Leerzeichen ausgeben werden sollen, sondern gegebenenfalls direkt nacheinander oder mit speziellen Trennzeichenfolgen, bietet print() den Parameter sep:

>>> print("Hello", name, "no_space", "in_between", sep="")

HelloMichaelno_spacein_between

>>> print("Important", "Message", "INFO", sep=" --- ")

Important --- Message --- INFO

Besonderheit: ZeilenumbruchBislang werden die Ausgaben mit print() automatisch mit Zeilenumbruch beendet. Wenn man allerdings mehrere Werte hintereinander in einer Zeile darstellen möchte, dann ist das Standardverhalten unpraktisch. Dieses lässt sich mit Angabe eines leeren Endezeichens in Form von end="" abwandeln – dadurch erfolgt auf der Konsole kein Zeilenumbruch mehr, weshalb die Eingabeaufforderung (>>>) direkt nach dem letzten ausgegebenen Zeichen steht:

>>> print("Hello " + name, end="")

Hello Michael>>>

Varianten der formatierten Ausgabe

Zur Ausgabe von Werten mit print() müssen diese in eine textuelle Form überführt werden. Das geschieht automatisch, wenn man die zuvor gezeigte kommaseparierte Variante nutzt. Alternativ kann man die Bestandteile durch einen Aufruf der Python-Standardfunktionalität str() in einen String wandeln und dann mit + verknüpfen. Dabei muss man aber auf die korrekte Angabe der Leerzeichen zum Abstand achten.

Egal welche der Varianten man wählt, alle haben so ihre Tücken. Als Abhilfe bietet Python zur formatierten Aufbereitung diverse Möglichkeiten.

>>> print("Hello %s the answer is %d and not %d" % (name,

the_answer, age))

Hello Michael the answer is 42 and not 18

>>>

>>> print("Hello {} the answer is {} and not {}".format(name,

the_answer, age))

Hello Michael the answer is 42 and not 18

>>>

>>> print(f"Hello {name} the answer is {the_answer} and not {age}")

Hello Michael the answer is 42 and not 18

Zunächst sehen wir die Variante mit %, dann {} in Kombination mit format() sowie schließlich die sogenannten f-Strings, wo man benannte Platzhalter in einem String nutzt, dem ein f vorangestellt ist – Details zu diesen Möglichkeiten erfahren Sie später in Abschnitt 3.1.3.

2.2.4Bezeichner (Variablennamen)

Ohne es explizit zu erwähnen, haben wir uns bei der Benennung von Variablen an ein paar Regeln gehalten. Zunächst einmal muss jede Variable (und auch die später vorgestellten Funktionen, Methoden und Klassen) durch einen eindeutigen Namen, auch Identifier oder Bezeichner genannt, gekennzeichnet werden.

Bei der Benennung sollte man folgende Regeln und Hinweise beachten:

Übrigens spielt es für Python keine Rolle, ob Sie sehr kurze (i, m, p), kryptische (dpy, mtr) oder aber gut verständliche Namen (days_per_year, max_table_rows) nutzen. Für Sie und das Verständnis beim Nachvollziehen eines Python-Programms macht das aber einen himmelweiten Unterschied. Natürlich sind kurze Variablennamen wie etwa x und y zur Bezeichnung von Koordinaten vollkommen verständlich, aber was sagt beispielsweise ein einzelner Buchstabe m aus? Schauen wir uns ein Beispiel an, das Ihnen das Gesagte verdeutlichen soll:

>>> # Gut verständliche Namen

>>> minutes_per_hour = 60

>>> days_per_year = 365

>>>

>>> # Nicht wirklich verständlich, was die Abkürzungen bedeuten

>>> m = 60

>>> dpy = 365

>>> # ACHTUNG: ziemlich schwierig unterscheidbar

>>> arrival_Time = "16:50"

>>> arrival_time = 16.50

Die letzten beiden Variablendefinitionen sind ziemlich ungünstig, da man sich beim Unterscheiden schwertut. Dadurch kommt es vermutlich leichter zu Fehlverwendungen.

Im Listing sehen wir sogenannte Kommentare. Damit kann man erklärende Zusatzinformationen hinterlegen. In diesem Fall werden mit # einzeilige Kommentare eingeleitet. Alles, was nach dem # bis zum Ende der Zeile folgt, wird bei der Ausführung von Python ignoriert (vgl. Abschnitt 2.7).

2.3Operatoren im Überblick

Operatoren beschreiben Aktionen (Operationen) zwischen Variablen und/oder Werten. Die Addition ist ein Beispiel, nämlich für den Operator +. Das lässt sich wunderbar mit der Python-Kommandozeile nachprüfen:

>>> sum1 = 200 + 50

>>> sum2 = sum1 + 500

>>> sum3 = sum2 + 750

>>> print("Summen: 1:", sum1, "/ 2:", sum2, "/ 3:", sum3)

Summen: 1: 250 / 2: 750 / 3: 1500

Generell bietet Python folgende Varianten von Operatoren:

2.3.1Arithmetische Operatoren

Die arithmetischen Operatoren sind uns größtenteils aus der Schule bekannt. Neben den Grundrechenarten Addition, Subtraktion, Multiplikation und Division gibt es noch die Modulo-Berechnung sowie die Potenzierung. Tabelle 2–1 bietet einen Überblick.

Beispiele für Grundrechenarten

Betrachten wir einfache Beispiele für diese Operationen, exemplarisch für Ganzzahlen als Parameter. Beachtenswert dabei ist insbesondere, dass die einfache Division eine Gleitkommazahl als Ergebnis liefert:

>>> print(9 - 7)

2

>>> print(7 + 2)

9

>>> print(7 * 2)

14

>>> print(15 / 4)

3.75

Schauen wir uns ergänzend die in anderen Sprachen (oft) unbekannten Operatoren an, nämlich ** zur Potenzierung und // zur Ganzzahldivision:

>>> print(2 ** 8)

256

>>> print(10 // 7)

1

>>> print(1 + 6 * 7 - 7 // 6)

42

Das Ergebnis der letzten Berechnung könnte Sie vielleicht überraschen. Überlegen wir kurz, um es zu verstehen: In Python gelten genau wie in der Mathematik die Vorrangregeln, also Punkt- vor Strichrechnung. Dadurch kommt es zu folgender Auswertung: 1+(6*7)1. Damit wir keine Nachkommastellen erhalten, nutzen wir mit // die Ganzzahldivision.

Tab. 2–1: Arithmetische Operatoren

Operator

Name

Beschreibung und Beispiel

+

Addition

Addiert zwei Werte:

x + y, z. B. 2 + 5 = 7

-

Subtraktion

Subtrahiert zwei Werte:

x - y, z. B. 9 - 2 = 7

*

Multiplikation

Multipliziert zwei Werte:

x * y, z. B. 2 * 7 = 14

**

Potenzierung

Potenziert zwei Werte (xy):

x "* y, z. B. 2 ** 8 = 256

/

Division

Dividiert zwei Werte:

x / y, z. B. 15 / 4 = 3.75

//

Ganzzahldivision

Dividiert zwei Werte ohne Rest:

x // y, z. B. 9 // 7 = 1

%

Modulo

Berechnet den Rest der Division zweier Werte:

x % y, z. B. 9 % 7 = 2

Spezialfall: Division durch 0

Bei der Division erinnern wir uns vielleicht an den Spezialfall der verbotenen Teilung durch 0. Das ist in der Mathematik nicht erlaubt bzw. liefert den Wert (unendlich). Mit diesem Wert kann man aber nicht mehr weiterrechnen. Auch Python verbietet eine derartige Division und reagiert darauf mit dem Auslösen eines ZeroDivisionError. Das Thema Fehlerbehandlung wird später in Kapitel 9 vertieft.

>>> 7.0 / 0.0

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: float division by zero

>>> 0.0 / 0.0

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: float division by zero

Hier reicht uns erst einmal das Wissen, dass nach einer solchen Fehlersituation die Ausführung eines Python-Programms gestoppt wird. In der Kommandozeile hingegen können Sie nach einem solchen Fehler problemlos weitere Kommandos eintippen.

Modulo-Operator

Schauen wir uns für einige Zahlen noch die Modulo-Berechnung an, die den Divisionsrest ermittelt:

>>> 5 % 1

0

>>> 5 % 2

1

>>> 5 % 3

2

>>> 5 % 4

1

>>> 5 % 5

0

Anstelle der hier gewählten Kurzform können wir natürlich auch erst Zahlen einer Variablen zuweisen und danach dann die Operationen ausführen:

>>> five = 5

>>> remainder = 9 % five

>>> remainder

4

Übrigens …

Der Modulo-Operator arbeitet auch auf Gleitkommazahlen. Wenn man beispielsweise ermitteln möchten, welche Restzeit sich bei Etappen von 45 Minuten und einer Fahrtzeit von 6,5 Stunden ergibt, dann kann der Modulo-Operator gute Dienste leisten:

>>> 6.5 % 0.75

0.5

2.3.2Zuweisungsoperatoren

Bei den Zuweisungsoperatoren denkt man natürlich zunächst an die einfache Zuweisung mit dem Operator =, den wir nun schon einige Male genutzt haben und der sich ziemlich natürlich erschließt: Er weist einer Variablen, hier clicks_per_hour, den rechts hinter dem = stehenden Wert, in diesem Fall 1234567, zu:

>>> clicks_per_hour = 1234567

Nehmen wir nun die Variable x und die Wertzuweisung von 100 als Beispiel:

>>> x = 100

Um einen Wert, etwa 10, hinzuzufügen, können wir Folgendes schreiben:

>>> x = x + 10

>>> x

110

Kurzschreibweisen

Manchmal möchte man die Addition eines Werts kompakter als beispielsweise

>>> x = x + 10

notieren. Dazu existiert als Kurzform der Additionszuweisungsoperator +=:

>>> x += 10

>>> x

120

Für diese Kurzschreibweise gibt es noch die im Anschluss weiter unten demonstrierten praxisrelevanten Varianten +=, -=, *=, **=, /=, //= und %=, die vorab in der folgenden Tabelle aufgeführt sind.

Tab. 2–2: Zuweisungsoperatoren

Operator

Name

Beschreibung

+=

Addition

Addiert zwei Werte:

x += y image x = x + y

-=

Subtraktion

Subtrahiert zwei Werte:

x -= y image x = x - y

*=

Multiplikation

Multipliziert zwei Werte:

x *= y image x = x * y

**=

Potenzierung

Potenziert zwei Werte (xy):

x **= y image x = x ** y

/=

Division

Dividiert zwei Werte:

x /= y image x = x / y

//=

Ganzzahldivision

Dividiert zwei Werte ohne Rest:

x //= y image x = x // y

%=

Modulo

Berechnet den Rest der Division zweier Werte:

x %= y image x = x % y

Viele der Operationen verdeutlicht folgendes Beispiel:

>>> counter = 0

>>> counter += 100

>>> counter

100

>>> counter -= 10

>>> counter

90

>>> counter /= 9

>>> counter

10.0

>>> counter *= 7

>>> counter

70.0

>>> counter %= 9

>>> counter

7.0

>>> counter **= 2

>>> counter

49.0

2.3.3Vergleichsoperatoren

Aus der Mathematik kennen wir diverse Operatoren, um Werte miteinander zu vergleichen. Exakt so funktioniert das auch in Python. Man möchte beispielsweise als wahr oder falsch abbilden können, ob zwei Werte gleich oder größer gleich sind. Für derartige Aussagen dienen die Wahrheitswerte True oder False vom Typ bool.

Tab. 2–3: Vergleichsoperatoren

Operator

Name

Beschreibung

==

gleich

x == y

!=

ungleich

x != y

>

größer

x > y

>=

größer gleich

x >= y

<

kleiner

x < y

<=

kleiner gleich

x <= y

Schauen wir uns wieder eine Abfolge von Aktionen zur Verdeutlichung an:

>>> 7 == 8

False

>>> 7 != 8

True

>>> 7 < 8

True

>>> 7 <= 8

True

>>> 7 > 8

False

>>> 7 >= 8

False

Wir können selbstverständlich Variablen vom Typ bool basierend auf den Ergebnissen erzeugen und abhängig vom Wert gewisse Aktionen ausführen. Dazu werden wir in Kürze Fallunterscheidungen und bedingte Ausführungen einsetzen.

>>> is_equal = 7 == 42

>>> is_equal

False

>>> age = 18

>>> younger_than30 = age < 30

>>> younger_than30

True

2.3.4Logische Operatoren

Um einfache Bedingungen zu prüfen, haben wir gerade die Vergleichsoperatoren kennengelernt. Oftmals soll nicht nur eine, sondern es müssen mehrere Bedingungen erfüllt sein, oder eine aus mehreren. Mitunter muss auch die Bedingung invertiert werden: Aus größer gleich 18 (>= 18) wird kleiner 18 (< 18). Mithilfe der logischen Operatoren lassen sich diese Aktionen in Python formulieren. Die Operatoren and und or arbeiten auf zwei booleschen Werten (bool), der Operator not auf einem.

Tab. 2–4: Logische Operatoren

Operator

Name

Beschreibung

and

UND

True, wenn beide Bedingungen erfüllt sind, ansonsten False

or

ODER

True, wenn mindestens eine der Bedingungen erfüllt ist, ansonsten False

not

NEGATION

True, wenn die Bedingung nicht erfüllt ist, ansonsten False

Schauen wir uns wieder eine Abfolge von Aktionen zur Verdeutlichung an. Zunächst definieren wir die zwei Ganzzahlvariablen age und points und kombinieren dann einige Abfragen – dabei sehen wir, dass bei and beide Bedingungen erfüllt sein müssen, damit True geliefert wird, und bei or zumindest eine der beiden:

>>> age = 27

>>> points = 127

>>>

>>> age > 20 and points > 100

True

>>> age > 20 and points > 200

False

>>>

>>> age > 30 or points > 100

True

>>> age > 20 or points > 200

True

>>> age > 30 or points > 200

False

>>>

>>> not points > 500

True

Mehrere Verknüpfungen

Es lassen sich nicht nur einzelne Werte miteinander verknüpfen, sondern auch ganze Ausdrücke, z. B. zur Prüfung der Möglichkeit, ein weiteres Bier in einer Bar zu bestellen. Dies soll gegeben sein, wenn das Alter >= 18 ist und man zudem noch genug Bares hat oder der Sitznachbar die Runde übernimmt:

one_more_beer = (age >= 18) and (credit > 0 or paid_by_neighbour)

Besonderheit bei Verknüpfungen

Wenn man eine Variable auf zwei Grenzen prüfen möchte, dann lässt sich das mit zwei Vergleichen kombiniert mit and lösen:

>>> age = 22

>>> age >= 18 and age <= 65

True

In Python gibt es noch eine sehr praktische Besonderheit, die gerade bei Vergleichen auf untere und obere Grenzen recht hilfreich und deutlich besser lesbar ist:

>>> 18 <= age <= 65

True

2.4Fallunterscheidungen

Unsere bisherigen kleinen Python-Experimente liefen alle Zeile für Zeile von oben nach unten ab. In der Praxis muss man oft Bedingungen prüfen, etwa ob eine Person älter als 18 Jahre ist oder ob die Bestellmenge größer als 100 ist und ein Rabatt gewährt wird.

Zur Formulierung derartiger Bedingungen haben wir gerade verschiedene Operatoren kennengelernt. Um abhängig von deren Auswertung einige Programmzeilen nur bedingt auszuführen, dient die if-Anweisung. Damit kann man steuern, dass nur beim positiven Ausgang der Auswertung die nachfolgenden Anweisungen ausgeführt werden. Um diese bedingt auszuführenden Anweisungen von den restlichen Anweisungen abzugrenzen, bedient man sich sogenannter Blöcke. Im Gegensatz zu vielen anderen Programmiersprachen, die hierzu geschweifte Klammern ({}) nutzen, werden Blöcke in Python durch eine Einrückung nachfolgender Anweisungen bestimmt. Oftmals wird dazu eine Einrückung von 4 Leerzeichen genutzt (laut PEP-8-Empfehlung2 ; zwingend ist lediglich eine konsistente Einrückung). Bitte beachten Sie, dass die Blöcke ausschließlich über die Einrückung definiert werden. Eine Zeile mit einer anderen Einrücktiefe beendet den Block bzw. stellt einen Syntaxfehler dar.

Bedingte Ausführung mit if

Wir haben nun genug Vorwissen, um eine bedingte Ausführung mehrerer Python-Anweisungen (genauer in Form eines Blocks) formulieren zu können. Dazu verwenden wir das Schlüsselwort if, eine Bedingung, gefolgt von einem Doppelpunkt, sowie einen Block mit den Anweisungen nach folgendem Muster:

>>> if condition:

... Aktionen

Sofern die Bedingung zu True ausgewertet wird, werden die Anweisungen innerhalb des Blocks abgearbeitet. Betrachten wir ein einfaches Beispiel, das prüft, ob die Multiplikation von 6*7 den Wert 42 ergibt, und in dem Fall eine Nachricht ausgibt:

>>> if 6 * 7 == 42:

... print("We found the answer!")

...

We found the answer!

Bei der Eingabe auf der Kommandozeile muss Python das Blockende erkennen. Zum Abschluss drücken Sie bitte zweimal Return – danach kommt es zur Ausführung.

Machen wir es etwas praxisrelevanter:

>>> age = 27

>>> if age >= 18:

... print("You are allowed to drive a car ...")

... print("- in case you have a driving license")

...

You are allowed to drive a car ...

- in case you have a driving license

Anhand der Ausgaben auf der Konsole erkennen wir, dass der Block ausgeführt wurde. Das ist der Fall, weil die Variable age den Wert 27 besitzt und damit der Vergleich if age >= 18 zu True ausgewertet wird.

if und else in Kombination

Oftmals möchte man nicht nur eine Bedingung mit if prüfen, sondern auch auf deren Nichterfüllung reagieren können. Das wird ansatzweise bereits an den vorherigen Beispielen deutlich. Vereinfachen wir das obige Beispiel, indem lediglich eine Wertzuweisung statt einer Konsolenausgabe erfolgt, und ergänzen den Negativfall mit else:

>>> age = 27

>>> if age > 18:

... result = "allowed"

... else:

... result = "NOT ALLOWED"

...

>>> result

'allowed'

Kurzschreibweise: Ternärer OperatorDiese Art der Abfrage ist etwas lang. Für derart einfache Fallunterscheidungen existiert mit dem sogenannten ternären Operator (vgl. Abschnitt 6.4) eine Kurzschreibweise, die folgende Struktur besitzt:

positiveCase if condition else negativeCase

Damit lässt sich die obige Zuweisung etwas kürzer schreiben:

>>> age = 27

>>> result = "allowed" if age > 18 else "NOT ALLOWED"

>>> result

'allowed'

if, elif und else in Kombination

Oftmals benötigt man neben einer Bedingung mit if noch weitere Prüfungen. Im nachfolgenden Beispiel wollen wir abhängig von einer Uhrzeit einen entsprechenden Gruß aufbereiten, also etwa »Good morning«, wenn es noch nicht 12 Uhr ist, oder aber »Good evening« ab 18 Uhr, aber vor 22 Uhr. Das können wir wie folgt mit if, elif und else in Kombination umsetzen – elif steht für else if in anderen Programmiersprachen.

Wenn der Umfang an abzutippenden Zeilen und Python-Kommandos zunimmt, empfiehlt es sich, auf eine IDE oder einen Texteditor umzusteigen, wie es der folgende Praxistipp thematisiert.

time = 15

if time < 12:

print("Good morning")

elif time < 18:

print("Good afternoon")

elif time < 22:

print("Good evening")

else:

print("Good night")

Good afternoon

Im Beispiel ist die Uhrzeit 15 nicht kleiner als 12, sodass die erste Bedingung False ist. Dagegen ist die zweite Bedingung True, sodass »Good afternoon« ausgegeben wird. Wäre die Uhrzeit nach 18 Uhr und vor 22 Uhr, erhielten wir »Good evening« und ansonsten resultiert die Auswertung in »Good night« als Ausgabe.

Hinweis: Komfortablere Programmeingabe

Für etwas umfangreichere Python-Programme bietet es sich an, in die PyCharm-IDE zu wechseln, weil diese das Editieren von mehrzeiligen Anweisungen komfortabel erlaubt, was eine Schwäche vom Python-Kommandozeileninterpreter ist. Alternativ können Sie die Zeilen in einem Texteditor abtippen und als Datei speichern. Diese lassen sich dann wie in Abschnitt 1.2.5 beschrieben als Skript ausführen. Für den Fall, dass ich einen dieser Bearbeitungsmodi empfehle, sind in den Programmlistings nicht mehr die Indikatoren >>> der REPL gezeigt, sondern lediglich die einzutippenden Kommandos.

Rekapitulation

Insgesamt bietet Python die folgenden bedingten Anweisungen:

2.5Funktionen

Wir haben schon einiges Basiswissen zusammengetragen. Sie erinnern sich sicher an die schon genutzten Aufrufe der in Python bereits vordefinierten Funktionen print(), type() und str() – man spricht auch von Built-in-Funktionen.

Nehmen wir nun einmal an, wir wollten eine Art Taschenrechner programmieren bzw. in unserem Programm immer wieder ähnliche Berechnungen anstellen. Obwohl es etwa bei einfachen Additionen noch problemlos möglich ist, wiederholt die gleichen Zeilen zu schreiben, ändert sich die Situation bereits bei einer Funktionalität, die ein paar Vergleiche oder andere Aktionen enthält. Um Funktionalität unter einem Namen zu bündeln, können wir dafür benutzerdefinierte Funktionen erstellen.

Funktionen definieren

Eine Funktion ist ein benanntes Stück Sourcecode, also eine Folge von Anweisungen, die nur dann abgearbeitet werden, wenn die Funktion aufgerufen wird. An diese können Sie Eingabedaten, sogenannte Parameter, übergeben. Funktionen werden verwendet, um bestimmte Aktionen auszuführen, und zwar auf wiederverwendbare Art und Weise. Das Ganze besitzt folgende Syntax:

def funktion_name(Parameter1, Parameter2, ...):

Anweisung1

Anweisung2

Definieren Sie eine Funktion also einmal und verwenden Sie die Aktionen viele Male überall dort im Python-Programm, wo dies benötigt wird.

Funktionen aufrufen

Um eine Funktion, und damit die dort enthaltenen Anweisungen, auszuführen, nutzen wir deren Namen, eine öffnende runde Klammer, optional eine kommaseparierte Liste von Parametern und zum Abschluss eine schließende runde Klammer. Für zwei Parameter sieht das wie folgt aus:

funktion_name(parameter_wert1, parameter_wert2)

Klingt noch etwas abstrakt, im Anschluss machen wir das Ganze konkreter mit der Standardfunktionalität print() und mit drei Parametern, wie wir es ähnlich zuvor schon getan haben:

print("Michael", "likes", "Python")

2.5.1Eigene Funktionen definieren

Greifen wir das Beispiel des einfachen Taschenrechners wieder auf. Schauen wir uns als Beispiele die Funktionen zur Addition und Multiplikation an:

>>> def add(value1, value2):

... return value1 + value2

...

>>> def mult(value1, value2):

... return value1 * value2

...

>>> result = add(7, 2)

>>> mult(result, 3)

27

Genauso wäre es möglich, die in den vorher gezeigten Altersprüfungen genutzte Abfrage als verständliche Funktion bereitzustellen:

>>> def is_adult(age):

... return age >= 18

...

Wir erkennen Folgendes: Funktionen bestehen aus einem Block mit einer oder mehreren Anweisungen. Um beim Aufruf Informationen übergeben zu können, dienen Parameter, die wiederum Typen besitzen. Insgesamt erlauben es uns Funktionen, wiederkehrende Funktionalität zu bündeln und über den Namen anzusprechen, statt die Sourcecode-Zeilen im Programm wiederholen zu müssen.

Spezialfall: Keine Rückgabe

Aufgerufene Funktionen dienen oftmals dazu, eine Aufgabe auszuführen, die ein Ergebnis berechnet und dieses dem Aufrufer zurückliefert, etwa für eine Addition. Dazu wird das Schlüsselwort return genutzt. Manchmal, etwa wie bei der Funktion print(), erhalten wir keine Rückgabe. In derartigen Fällen gibt es dann allerdings kein return mit Wertangabe, sondern nur eine oder mehrere Anweisungen:

>>> def greet(name):

... print("Hello", name)

...

>>> greet("Michael")

Hello Michael

Kombination mit Bedingungen

Im nachfolgenden Beispiel wollen wir als wiederverwendbare Funktionalität das Minimum von drei Ganzzahlen ermitteln, zu deren Repräsentation wir x, y und z als Parameternamen verwenden.3

def min_of_3(x, y, z):

if x < y:

if x < z:

return x

else:

return z

else:

if y < z:

return y

else:

return z

Mal ganz ehrlich: Sind Sie allein beim Betrachten sicher, dass sich dort kein Flüchtigkeitsfehler eingeschlichen hat? Zumindest der Programmablauf (Kontrollfluss) ist doch ein wenig unübersichtlich. Einfacher geht das Ganze, wenn man vordefinierte Bausteine verwendet, was wir uns im Anschluss anschauen. Zuvor probieren wir die obige Implementierung der Funktionalität einmal für verschiedene Wertekombinationen aus:

min_of_3(1,2,3)

1

min_of_3(11,2,3)

2

min_of_3(11,22,3)

3

Funktionen als wiederverwendbare Bausteine

Wir könnten beispielsweise mit folgender Implementierung der Funktion min_of() zur Berechnung des Minimums für zwei Zahlen starten:

def min_of(x, y):

if x < y:

return x

else:

return y

Tatsächlich ließe sich das mit dem auf Seite 63 kurz vorgestellten ternären Operator (Details finden Sie dann in Abschnitt 6.4) noch kompakter schreiben:

def min_of_shorter(x, y):

return x if x < y else y

Wenn man diese Bausteine besitzt, kann man die ursprüngliche Funktion min_of_3() und das komplizierte if-Gebilde folgendermaßen vereinfachen:

def min_of_3(x, y, z):

return min_of(x, min_of(y, z))

Überprüfen wir kurz, ob die gleichen Werte wie zuvor geliefert werden:

min_of_3(1,2,3)

1

min_of_3(11,2,3)

2

min_of_3(11,22,3)

3

Perfekt, funktional ist das gleichwertig, aber es war durch den Einsatz der Funktionen viel weniger Aufwand und ist deutlich kürzer. Auch die Verständlichkeit ist um Längen besser, da wir nicht mehrfach verschachtelte if-Gebilde nachvollziehen müssen.

Nebenbei ist es immer eine gute Idee, sich ein wenig in den Python-Basisbausteinen umzuschauen, weil es dort eine Vielzahl an nützlichen Funktionalitäten bereits vordefiniert gibt. In unserem Fall könnten wir die in Python vordefinierte Funktion min() zur Berechnung des Minimums nutzen.

2.5.2Nützliche Beispiele aus Python

Wir haben bereits die praktische Funktion print() kennengelernt. Betrachten wir, was man beispielsweise für mathematische Funktionalitäten sonst noch so in Python findet:

Vorteile durch den Einsatz der Built-in-Funktionen

Mit den bereits in Python vordefinierten Funktionen können wir die Implementierung von min_of_3_python_built_in() wie folgt vereinfachen – wir müssen nicht mal eine Funktion min() selbst schreiben, sondern nutzen die vordefinierte aus Python:

def min_of_3_python_built_in(x, y, z):

return min(x, min(y, z));

Hier mag der Vorteil noch nicht ganz so klar werden, da es sehr einfach war, eine min()-Funktion zu implementieren. Je komplexer die Funktionalität jedoch wird, desto eher schleichen sich auch Fehler ein. Praktischerweise sind die Bausteine von Python jedoch gründlich getestet.

2.6Fehlerbehandlung und Exceptions

Während der Abarbeitung von Python-Programmen kann es zu Fehlersituationen kommen. Zu deren Beschreibung dienen sogenannte Exceptions. Sie werden etwa intern ausgelöst, wenn eine Datei oder eine Position in einem String nicht existiert, auf die man zugreifen möchte, oder wenn eine mathematische Berechnung ungültig ist.

Schauen wir uns zum ersten Verständnis die Umwandlung zweier Texte in den Typ int an, einmal rein mit Ziffern und einmal für Buchstaben und Ziffern gemischt:

>>> int("7271")

7271

>>> int("E605")

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ValueError: invalid literal for int() with base 10: 'E605'

Die erste Umwandlung ist problemlos möglich, aber der Versuch, den Text E605 in eine Ganzzahl zu wandeln, führt (selbstverständlich) zu einem Problem. Das resultiert in einem Programmabbruch und produziert einen sogenannten Traceback, eine Protokollierung der Aufrufhierarchie bis zur fehlerhaften Programmstelle. Dort sehen wir, dass es zu einem in Python vordefinierten ValueError kommt. Sind die Anweisungen in ein Programm eingebettet, so werden die Zeilen nach einer solchen Fehlersituation ohne spezielle Behandlung nicht mehr ausgeführt und die Abarbeitung unseres Programms gestoppt. Mehr Details erfahren Sie in Kapitel 9. Auf der Kommandozeile kann man dagegen problemlos weitere Anweisungen eingeben.

2.7Kommentare

In den vorangegangenen Beispielen haben wir mitunter bereits Kommentare genutzt, hier möchte ich ein paar Varianten davon zeigen.

Ein Kommentar ist ein Teil Ihres Python-Programms und sollte hilfreiche Anmerkungen enthalten, wird jedoch bei der Ausführung ignoriert. In Python markiert # den Beginn einer Kommentarzeile. Wenn mehrere aufeinanderfolgende Zeilen kommentiert werden sollen, muss das Symbol # an den Anfang jeder Zeile gesetzt werden. Man kann auch mehrzeilige, in drei Anführungszeichen eingerahmte Strings (vgl. Abschnitt 3.2.3) als Kommentar nutzen:

>>> # Kommentar

>>> age = 11 # Zeilenkommentar

>>>

>>> # Blockkommentar 1

>>> # Blockkommentar 2

>>> # Blockkommentar 3

>>>

>>> """

... Kommentarzeile 1

... Kommentarzeile 2

... KommentarZeile 3

... """

'\nKommentarzeile 1\nKommentarzeile 2\nKommentarZeile 3\n'

2.8Module

Bislang haben wir lediglich ein paar Anweisungen innerhalb des Python-Kommandozeileninterpreters ausgeführt. Dieser ist wunderbar für erste Experimente geeignet. Aber wie gehen wir damit um, wenn wir die erstellten Funktionalitäten später nochmals wiederverwenden möchten? Dazu hatten wir Funktionen kennengelernt. Jedoch gibt es mit unserer bisherigen Arbeitsweise ein kleines Problem: In dem Moment, wo wir den Python-Kommandozeileninterpreter wieder beenden, sind unsere ganzen Mühen futsch. Alle Variablen und Funktionen sind verloren. Das wäre sehr schade, wenn wir gerade beispielsweise eine nützliche Sammlung mathematischer Funktionalitäten oder ein kleines Spiel implementiert hätten.

Wollen wir eine Funktionalität auch später bereitstellen können, so müssen wir diese in einer Datei speichern. Dateien mit Python-Anweisungen nennt man Module. Für Python sollten diese die Endung .py besitzen. Außerdem sollten Modulnamen allgemein in Kleinbuchstaben geschrieben werden. Zur Bearbeitung des Sourcecodes eines Moduls kann man zwar einen Texteditor nutzen, geeigneter ist jedoch eine IDE. Das gilt insbesondere, wenn die Programme umfangreicher werden. Dann bietet es sich an, gewisse Funktionalitäten auf verschiedene Dateien aufzuteilen und semantisch zu gruppieren. Ebenso kann die Motivation sein, praktische Funktionalitäten nicht immer wieder zu kopieren, sondern einmal als Datei bereitzustellen.

Rekapitulieren wir: Eine Datei mit Python-Anweisungen wird als Modul bezeichnet – in der Softwarearchitektur versteht man unter Modulen eigentlich etwas anderes, nämlich in sich abgeschlossene Programmeinheiten mit klaren Schnittstellen und einer sauberen Definition von Abhängigkeiten, aber in Python ist die Begrifflichkeit nun einmal so, wie sie ist.

2.8.1Imports – Einbinden anderer Funktionalitäten

Python-Programme oder Module können wiederum Funktionalitäten aus anderen Modulen nutzen. Dazu ist ein sogenannter Import nötig. Um in das aktuelle Programm ein externes Modul, also eine andere .py-Datei, zu integrieren, ist folgende Syntax mit import üblich, wobei der Modulname ohne die Endung .py angegeben wird:

import modulname

Beispiel

Das Einbinden anderer Module schauen wir uns am Beispiel des Moduls random zum Erzeugen von Zufallszahlen an. Dort existieren mehrere Funktionen unter anderem randrange(). Probieren wir einmal einen direkten Zugriff aus:

>>> random.randrange(10, 100)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: name 'random' is not defined

Offensichtlich und logischerweise, da bislang noch kein passender Import erfolgt ist, können wir nicht auf die Funktionalität zugreifen. Es erfordert folgenden Import:

>>> import random

Danach rufen wir die Funktionen auf – außerdem hat man durch den Import auch Zugriff auf andere Funktionen wie choice() zur zufälligen Auswahl aus einer Werteangabe:

>>> random.randrange(10, 100)

24

>>> random.randrange(10, 100)

63

>>> random.choice(["Napoli", "Funghi", "Diavolo"])

'Diavolo'

Eingeschränkter Import

Möchte man den Import auf eine Funktionalität einschränken und darauf direkt mit dem Namen ohne Angabe des Modulnamens zugreifen, so schreibt man Folgendes:

>>> from random import randrange

>>> randrange(10, 100)

89

Ein direkter Aufruf von choice() ist dann allerdings nicht möglich, weil der Name ja nicht importiert wurde:

>>> choice(["Napoli", "Funghi", "Diavolo"])

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: name 'choice' is not defined

Es ist aber auch problemlos möglich, mehrere Funktionalitäten durch kommaseparierte Angabe einzubinden, hier, um zusätzlich choice() zur Verfügung zu stellen:

>>> from random import randrange, choice

>>>

>>> choice(["Napoli", "Funghi", "Diavolo"])

'Funghi'

2.8.2Zusammenfassung und Ergänzendes

Fassen wir kurz zusammen: Ein Modul entspricht in Python einer Datei, die Python-Kommandos, also beispielsweise Variablendefinitionen, Funktionen und weitere Anweisungen, enthält. Der Modulname ergibt sich aus dem Dateinamen, allerdings ohne die Endung .py.

Praktischerweise kann man innerhalb eines Moduls auf den Modulnamen über die Variable __name__ zugreifen, die automatisch bei einem Import zugewiesen wird.

Wenn Module von anderen Modulen importiert werden, dann werden die dort enthaltenen Anweisungen einmalig von oben nach unten abgearbeitet. Dadurch werden alle darin aufgelisteten Funktionen zum Zeitpunkt des Importierens aufgerufen und ausgeführt.

Besonderheiten beim Start und beim Import

Für den Fall, dass eine Python-Datei mit dem Kommandozeileninterpreter aufgerufen oder aus der IDE gestartet wird, so enthält __name__ den Namen __main__. Das ist für folgende Situation nützlich: In größeren Programmen ist das Verhalten, dass die Python-Anweisungen eines Moduls beim Importieren einmal abgearbeitet werden, in der Regel hinderlich. Soll also die Funktionalität bei einem Import nicht aufgerufen werden, so kann man dies durch folgenden Trick erreichen:

def main():

# Hauptprogramm

if __name__ == "__main__":

main()

Dies ist insbesondere dann nützlich, wenn Module eigenständig aufgerufen oder aber in ein anderes Programm eingebettet werden sollen.

2.9Built-in-Datentypen

In Python sind mit Listen (list), Mengen (set) und Dictionaries (dict) die in der Programmierpraxis gebräuchlichsten Datenstrukturen direkt in die Sprache integriert, sie sind also built-in. Das gilt ebenfalls für Tupel (tuple).

Man spricht in dem Zusammenhang auch von Collection-Literalen, weil man die Datenstrukturen einfach so textuell in eckigen bzw. geschweiften Klammern mit direkter Werteangabe notiert, wie wir es im Anschluss etwas genauer sehen werden.

2.9.1Listen (list)

Sollen mehrere Werte, etwa Namen, Städte, Aufgaben, Kunden usw. verwaltet werden, dann sind Listen oftmals eine gute Wahl – aus dem realen Leben kennen wir etwa Einkaufslisten, To-do-Listen, Wunschlisten und die guten Vorsätze für das neue Jahr :-)

In Python können Listen mehrere Werte speichern, die zudem eine Reihenfolge besitzen und auch Duplikate enthalten können. Listen werden in eckigen Klammern in Form einer kommaseparierten Aufzählung von Werten wie folgt definiert:

>>> names = ["Tim", "Michael", "Tom", "Michael"]

Alternativ kann man eine leere Liste mit folgenden beiden Varianten erzeugen:

>>> empty_list_1 = []

>>> empty_list_2 = list()

Für Listen erfolgt der lesende und schreibende Zugriff über die Angabe eines Index in eckigen Klammern – die vorderste Position besitzt den Wert 0:

>>> names[0]

'Tim'

>>> names[2] = ">>Tommy<<"

>>> names

['Tim', 'Michael', '>>Tommy<<', 'Michael']

Die Anzahl an Elementen ermittelt man durch Aufruf von len(). Einzelne Werte kann man mit append() hinzufügen – um eine Liste von Werten zu ergänzen, nutzt man +=:

>>> len(names)

4

>>> names.append("Mike")

>>> names

['Tim', 'Michael', '>>Tommy<<', 'Michael', 'Mike']

>>> names += ["Willi", "Yves"]

>>> names

['Tim', 'Michael', '>>Tommy<<', 'Michael', 'Mike', 'Willi', 'Yves']

2.9.2Tupel (tuple)

Tupel dienen dazu, mehrere unterschiedliche Informationen zu einer Einheit zu bündeln. Tupel sind damit ähnlich zu Listen, jedoch werden die Werte in runden Klammern aufgeführt und die Intention ist eine andere, nämlich die eines fixen Datenbestands fixer Größe – man kann Tupel grob als unveränderliche Listen ansehen.

>>> pizza_top_3 = ("diavolo", "napoli", "funghi")

Man kann ein Tupel auch aus einer Liste durch Aufruf von tuple() erzeugen:

>>> pizza_top_3 = tuple(["diavolo", "napoli", "funghi"])

>>> pizza_top_3

('diavolo', 'napoli', 'funghi')

Führen wir zum besseren Verständnis noch ein paar Aktionen aus, nämlich einen indizierten Zugriff, eine Ausgabe mit print() und eine Wertzuweisung:

>>> print(pizza_top_3[0])

diavolo

>>>

>>> print(pizza_top_3)

('diavolo', 'napoli', 'funghi')

>>>

>>> pizza_top_3[0] = "surprise"

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment

Das führt zu den obigen Ausgaben, die insbesondere zeigen, dass man die Werte innerhalb eines Tupels nachträglich nicht mehr ändern kann, weshalb die Wertzuweisung in einem Fehler resultiert.

Besonderheit: Tuple (Un-)Packing

Tupel weisen eine syntaktische Besonderheit auf. Man kann nämlich einfach auf der linken Seite mehrere Variablen kommasepariert auflisten und diese werden dann automatisch mit den von der Position korrespondierenden Werten aus dem Tupel belegt. Dieses praktische Verhalten nennt sich Tuple Unpacking:

>>> # Tuple Unpacking => elementweise in Variablen übertragen

>>> first, second, last = pizza_top_3

>>> print(first, second, last)

diavolo napoli funghi

Ebenso ist es möglich, einer Variablen eine kommaseparierte Folge von Werten zuzuweisen, wodurch die Variable zu einem Tupel wird. Hier spricht man von Tuple Packing:

>>> first_primes = (2, 3, 5, 7)

>>> print(first_primes)

(2, 3, 5, 7)

>>>

>>> # Tuple Packing => Werte als Tupel einer Variablen zuweisen

>>> first_primes_auto = 2, 3, 5, 7

>>> print(first_primes_auto)

(2, 3, 5, 7)

Hinweis: Besonderheit zu viele Elemente

Was passiert eigentlich, wenn rechts mehr Werte als links Variablen aufgeführt sind?

>>> one, two = 1, 2, 3, 4, 5

Das wird von Python erkannt und resultiert in einer Fehlermeldung. Mit folgender Schreibweise kann man eine Hilfsvariable definieren, die die überschüssigen Werte in Form einer Liste aufsammelt:

>>> one, two, *rest = 1, 2, 3, 4, 5

>>> rest

[3, 4, 5]

2.9.3Mengen (set)

Python ermöglicht die Modellierung mathematischer Mengen. Definitionsgemäß besitzen diese weder eine Reihenfolge noch Duplikate. Aus Ersterem folgt, dass man nicht über die Position zugreifen kann. Letzteres führt dazu, dass Duplikate beim Hinzufügen automatisch entfernt werden. Eine Menge von Werten lässt sich mit geschweiften Klammern wie folgt definieren:

>>> fruits = {"Apple", "Ananas"}

>>> fruits.add("Apple")

>>> fruits.add("Banana")

>>> fruits.add("Orange")

Schauen wir uns kurz den Inhalt der Menge an. Zudem prüfen wir mit in, ob ein Element enthalten ist, und ermitteln mit len() die Anzahl an Elementen:

>>> fruits

{'Banana', 'Orange', 'Apple', 'Ananas'}

>>>

>>> "Ananas" in fruits

True

>>> "Melon" in fruits

False

>>>

>>> len(fruits)

4

2.9.4Dictionaries (dict)

Die letzte wichtige Built-in-Datenstruktur sind die Dictionaries. Diese bilden Schlüssel auf Werte ab, etwa Namen auf Telefonnummern, Personen auf Hobbys usw. Der Zugriff erfolgt über die schon bekannte eckige Klammersyntax. Im Gegensatz zu Listen und Tupeln nutzt man aber dort den Wert des Schlüssels und keine Indexposition.

Betrachten wir als Beispiel die Abbildung von Namen auf Mobiltelefone. Wie bei Mengen erfolgt hier die Vorgabe von Schlüsseln und Werten in geschweiften Klammern, jedoch mit einem Doppelpunkt zwischen Schlüssel und Wert.

>>> person_phone = {"Tim" : "iPhoneSE2",

... "Michael" : "iPhone 13 Pro",

... "Marc": "iPhone X"}

>>>

>>> person_phone

{'Tim': 'iPhoneSE2', 'Michael': 'iPhone 13 Pro', 'Marc': 'iPhone X'}

>>>

>>> len(person_phone)

3

>>>

>>> person_phone["Marc"]

'iPhone X'

Im folgenden Beispiel sehen wir, dass es problemlos möglich ist, für die Werte wiederum Listen oder andere Container zu verwenden und diese wie für Listen gewohnt mit eckigen Klammern und der Angabe des Schlüssels auszulesen:

>>> person_hobbies = {"Tim" : ["Computer", "Java"],

... "Mike" : ["Java", "Python"],

... "Jim" : ["Swimming"]}

>>>

>>> person_hobbies

{'Tim': ['Computer', 'Java'], 'Mike': ['Java', 'Python'], 'Jim': ['Swimming']}

>>>

>>> len(person_hobbies)

3

>>>

>>> person_hobbies["Mike"]

['Java', 'Python']

Außerdem ist es durch eine einfache Zuweisung möglich, Schlüssel-Wert-Abbildungen hinzuzufügen:

>>> person_hobbies["James"] = ["Movies"]

>>> person_hobbies

{'Tim': ['Computer', 'Java'], 'Mike': ['Java', 'Python'], 'Jim': ['Swimming'], 'James': ['Movies']}

>>>

>>> len(person_hobbies)

4

Das war lediglich ein Schnelleinstieg für den ersten Überblick. Die Thematik vertiefen wir in den Kapiteln 5 und 7.

2.10Schleifen

Mitunter soll ein bestimmter Abschnitt des Sourcecodes mehrmals ausgeführt werden. Das wird mithilfe von Schleifen realisiert. Davon existieren diverse Varianten in Python, die wir nun kennenlernen wollen.

2.10.1Besonderheit: Ranges

Im Kontext der gleich vorgestellten Schleifen wird ab und an range(startIncl, endExcl) genutzt. Damit lassen sich Wertebereiche in den angegebenen Grenzen generieren. Das Besondere an diesen Wertebereichen ist, dass diese nicht vollständig erzeugt werden, sondern dass sukzessive bei Bedarf neue Werte geliefert werden. Möchte man die Werte des Wertebereichs direkt erstellen, so kann man dies mit einem Aufruf von list() ummanteln:

>>> range(1, 10)

range(1, 10)

>>> list(range(1, 10))

[1, 2, 3, 4, 5, 6, 7, 8, 9]

Soll ein Wertebereich von 0 starten, so kann man die untere Grenze weglassen, weil diese standardmäßig 0 ist. Dies ist hier für den Wertebereich 0–5 gezeigt:

>>> list(range(0, 5))

[0, 1, 2, 3, 4]

>>> list(range(5))

[0, 1, 2, 3, 4]

2.10.2Indexbasierte for-in-Schleife

Wenn Sie eine Folge von Anweisungen, also einen Block, eine gewisse Anzahl Mal ausführen wollen, so ist die for-Schleife mit der Angabe eines Wertebereichs per range(startIncl, endExcl) eine gute Wahl. Probieren wir es wieder aus:

>>> for i in range(2, 6):

... print("Durchlauf:", i)

...

Durchlauf: 2

Durchlauf: 3

Durchlauf: 4

Durchlauf: 5

Was passiert dabei im Detail?

Zunächst wird der Wertebereich vor dem Beginn der Schleife festgelegt und in diesem Fall dort der Variablen i der Startwert (hier i = 2) zugewiesen. Mithilfe des Wertebereichs wird indirekt eine Bedingung ausgedrückt, nämlich in diesem Fall i < 6. Dadurch wird festgelegt, wie häufig die Schleife ausgeführt werden soll (hier: i muss kleiner als 6 sein). Sofern die Bedingung gilt, wird die Zählvariable, hier i, nach jedem Durchlauf automatisch um den Wert 1 erhöht und die Schleife beginnt von vorne. Erreicht die Zählvariable den Grenzwert, wird die Schleife beendet. Die Erhöhung findet erst nach der vollständigen Abarbeitung des Blocks in der Schleife, auch Schleifenkörper oder -rumpf genannt, s tatt.

Variieren der Schrittweite

Für gewisse Anwendungsfälle soll der Schleifenzähler nicht um den Wert 1 erhöht werden, sondern man benötigt mehr Flexibilität, etwa um in 5er-, 10er- oder 100er-Schritten hochzuzählen. Diese Anforderung lässt sich mit Ranges leicht durch einen zusätzlichen Parameter erreichen: range(startIncl, endExcl), stepSize). Nachfolgend wird die Grenze auf 10 erhöht und eine Schrittweite von 3 verwendet – genauso ist es denkbar und einfach möglich, in 2er- oder 10er-Schritten voranzuschreiten.

>>> for i in range(2, 10, 3):

... print(i)

...

2

5

8

Negative Schrittweite

Mitunter benötigt man auch das Herunterzählen, etwa von 10 auf 0 oder von 3 auf 1. Letzteres implementieren wir durch Umkehren der Bereichsangaben, also die größere Zahl als erster Paramter im Range, und negativer Schrittweite folgendermaßen:

>>> for i in range(3, 0, -1):

... print(i)

...

3

2

1

Durchlaufen einer Liste

Gebräuchlicher als eine reine Wiederholung sind Schleifen über Datenbestände, etwa die Elemente einer Liste. Das lässt sich folgendermaßen mit einem indizierten Zugriff realisieren – allerdings ist das in Python stilistisch unschön, da elegantere und im Anschluss vorgestellte Varianten existieren.

>>> names = ["Barbara", "Lilija", "Sophie"]

>>> for i in range(len(names)):

... print(names[i])

...

Barbara

Lilija

Sophie

Besonderheit beim Durchlaufen einer Liste

Nachfolgend möchte ich noch auf eine Python-Besonderheit eingehen, nämlich in einer Schleife keinen Variablennamen anzugeben, sondern _. Damit wird ausgedrückt, dass dieser Wert im weiteren Verlauf nicht mehr benutzt wird:

>>> for _ in range(5):

... print("Again")

...

Again

Again

Again

Again

Again

Interessanterweise könnte man jedoch auch auf den Wert zugreifen – das widerspricht jedoch der eigentlichen Intention:

>>> for _ in range(5):

... print("Again", _)

...

Again 0

Again 1

Again 2

Again 3

Again 4

2.10.3Wertebasierte for-in-Schleife

Das Hantieren mit Indexwerten birgt Fehler. Möchte man nur eine vordefinierte Wertemenge durchlaufen (man spricht auch von Iterieren), so kann man bevorzugt statt eines Wertebereichs auch eine Liste oder eine direkte Angabe von Werten verwenden. Statt der Indexvariablen wird eine Variable mit dem Wert und einer Angabe der zu durchlaufenden Werte genutzt.

Nachfolgend definieren wir eine Liste, die wir nun ohne Index durchlaufen. Dazu dient die Variante mit for und in, wobei man dort eine Variable angibt, die das jeweilige Listenelement pro Schleifendurchlauf enthält:

>>> for current_name in ["Barbara", "Lilija", "Sophie"]:

... print(current_name)

...

Barbara

Lilija

Sophie

Was passiert dabei im Detail?

Für jeden Wert in der Liste wird dieser zunächst der Variablen current_name zugewiesen. Im Anschluss werden dann die Aktionen des Schleifenrumpfs, hier die Konsolenausgabe mit print(), ausgeführt. Das wiederholt sich, bis alle Werte aus names verarbeitet wurden.

Anmerkung

Der Vergleich der indexbasierten und der wertebasierten for-in-Schleife zeigt, dass letztere etwas einfacher zu formulieren ist, keine Indexzugriffe benötigt und dadurch lesbarer ist. Allerdings hat man in dem Fall dann (logischerweise) auch keinen Zugriff mehr auf den Index.

2.10.4Die for-in-enumerate-Schleife mit Index und Wert

Wir haben nun schon mehrere Arten gesehen, wie wir eine Schleife gestalten können. Folgende Varianten sollten uns mittlerweile geläufig sein:

message = ["Python", "has", "several", "loop", "variants"]

for i in range(len(message)):

print(i, message[i], end=',')

print()

for current_word in message:

print(current_word, end=',')

print()

In diesem Beispiel erinnern wir uns an den Parameter end für print() zur Spezifikation einer Trennzeichenfolge. Dadurch werden die Wörter kommasepariert ohne Zeilenumbruch hintereinander ausgegeben. Das führt zu folgenden Ausgaben:

0 Python,1 has,2 several,3 loop,4 variants,

Python,has,several,loop,variants,

Im ersten Fall haben wir Zugriff auf den Index und können somit die Position mit ausgeben, im zweiten Fall ist das nicht möglich, dafür ist die Iteration etwas klarer.

Manchmal benötigt man eben nicht nur Zugriff auf den Index, sondern auch direkt auf den Wert. Dazu bietet Python eine dritte Schleifenvariante, nämlich mit enumerate(), was sowohl Zugriff auf den Index als auch den Wert bietet:

>>> message = ["Python", "has", "several", "loop", "variants"]

>>> for i, current_word in enumerate(message):

... print(i, current_word, end=',')

...

0 Python,1 has,2 several,3 loop,4 variants,>>>

Das produziert wie die obere indexbasierte Variante die gleiche Ausgabe, allerdings ist die Schleife deutlich einfacher zu lesen und zu verstehen. Beachten Sie aber, dass hier durch das veränderte Endezeichen zum Schluss kein Zeilenumbruch erfolgt.

Beispiel für den Bedarf an Index und Wert

Schauen wir uns ein weiteres Beispiel an, das nach jedem ungeraden Zeichen einen Unterstrich in der Ausgabe ergänzt und jedes gerade Zeichen durch Aufruf von upper() großschreibt – hier lernen wir, dass auch Strings mit einer for-in-Schleife durchlaufen werden können:

>>> for i, ch in enumerate("fun with chars"):

... if i % 2 != 0:

... print(ch + "_", end="")

... else:

... print(ch.upper(), end="")

...

Fu_N _Wi_Th_ c_Ha_Rs_>>>

Wir erhalten folgende Ausgabe, die die zeichenweise Iteration4 zeigt:

Fu_N _Wi_Th_ c_Ha_Rs_

Wahl der Schleifenvariante

Bedenken Sie immer: Halten Sie den Sourcecode möglichst verständlich und einfach! Falls Sie keinen Zugriff auf den Index benötigen, dann empfiehlt sich die Variante mit in.

2.10.5Die while-Schleife

Die while-Schleife durchläuft einen Block so lange, wie eine angegebene Bedingung erfüllt ist. Im Gegensatz zur for-Schleife müssen bei Bedarf gewisse Initialisierungsarbeiten explizit vorab erfolgen. Gleiches gilt innerhalb des Blocks der Schleife (des Schleifenkörpers) auch für das Hochzählen bzw. Modifizieren der Schleifenvariablen, um die Bedingung später einmal zu False auszuwerten und dadurch die Schleife tatsächlich (irgendwann) zu beenden.

while Bedingung:

# auszuführender Block

Schauen wir uns dies konkret an einem Beispiel an:

>>> i = 0

>>> while i < 5:

... print(i)

... i += 1

...

0

1

2

3

4

Nochmals als Hinweis: Vergessen Sie nicht, die in der Bedingung verwendete Variable zu erhöhen, sonst wird die Schleife nie beendet!

Was passiert dabei im Detail?

Als Erstes definieren wir eine Schleifenvariable namens i mit dem Wert 0. Danach wird in while die Einhaltung der Bedingung (hier i < 5) geprüft. Solange dies gegeben ist, werden die Anweisungen des sogenannten Schleifenrumpfs ausgeführt. Darin erfolgt eine Konsolenausgabe und danach wird der Wert von i um 1 erhöht.

2.11Weiterführende Informationen

Python bietet ein wirklich lesenswertes Onlinetutorial. Dieses finden Sie unter https://docs.python.org/3/tutorial/. Neben einem Einstieg behandelt es diverse andere Themen recht ausführlich.