Kontrollstrukturen
Komplexe Programme laufen nicht linear ab (also Zeile für Zeile vom Anfang eines Scripts bis zum Ende), sondern beinhalten Verzweigungen und Schleifen. Diese sogenannten Kontrollstrukturen steuern den Programmfluss. Wesentlich dabei sind Vergleiche, die bestimmen, ob bestimmte Codezeilen ausgeführt werden oder nicht bzw. wie oft diese wiederholt werden.
Schon bei Funktionen haben wir beobachtet, dass Programmcode nicht linear ausgeführt wird, denn erst beim Aufrufen einer Funktion springt Python zu den entsprechenden Zeilen im Funktionskörper und führt diese aus.
Mit Hilfe des sogenannten Debuggers kann man in Visual Studio Code ein Script “in Zeitlupe” ausführen. Im Debug-Modus wartet der Python Interpreter nämlich nach jedem Befehl, bis der nächste manuell gestartet wird. Das gibt uns die Möglichkeit, den Ablauf des Programmes sowie dessen Zustand in jedem Schritt zu beobachten. Dabei geht man wie folgt vor:
- Zunächst klickt man links neben die erste Zeile, um einen Breakpoint zu setzen (dieser erscheint als roter Punkt). In dieser Zeile wird die Ausführung dann angehalten werden.
- Dann klickt man auf den Pfeil nach unten im Play-Button und wählt “Debug Python File” aus.
- Die Ausführung stoppt beim Breakpoint. Die aktuelle Zeile ist gelb hinterlegt und mit einem gelben Pfeil gekennzeichnet. Mit “Step Into” (F11) kann man zum nächsten Befehl springen.
- In der Variablen-Ansicht links sieht man alle lokalen Objekte (und deren aktuellen Werte), die im Programm angelegt werden.
Zum Ausprobieren versuchen Sie das folgende kurze Script zu debuggen:
In welcher Reihenfolge werden die Code-Zeilen ausgeführt?
Die Website PythonTutor kann den Progammfluss ebenfalls Schritt für Schritt darstellen. Dazu kopiert man einfach den gesamten Code in das Eingabefeld und klickt auf “Visualize Execution”. So kann man durch wiederholtes Klicken auf “Next” nachvollziehen, wie Python den Code ausführt.
Vergleiche
Vergleiche sind logische Ausdrücke – ihr Ergebnis ist entweder wahr (True
) oder falsch (False
). In Python gibt es dafür den Datentyp bool
. Folgende Vergleichsoperationen sind möglich:
- Gleichheit:
==
- Ungleichheit:
!=
- Kleiner:
<
- Kleiner gleich:
<=
- Größer:
>
- Größer gleich:
>=
Man kann mehrere logische Ausdrücke mit den folgenden Operatoren verknüpfen:
- Und-Verknüpfung:
and
- Oder-Verknüpfung:
or
Ein Ausdruck kann mit dem Operator not
logisch invertiert werden (d.h. aus True
wird False
und aus False
wird True
). Weiters gibt es noch folgende Operatoren:
is
: Identität (prüft ob zwei Objekte identisch sind, nicht nur deren Werte)in
: Prüft ob ein Wert in einer Sequenz enthalten ist
Während ==
zwei Werte miteinander vergleicht, überprüft is
zwei Objekte auf Gleichheit. Ein Objekt hat einen Wert, zwei verschiedene Objekte können denselben Wert haben. Die Funktion id
liefert eine eindeutige Nummer (ID) für ein Objekt zurück – zwei verschiedene Objekte haben immer eine unterschiedliche ID (ansonsten handelt es sich um ein und dasselbe Objekt).
Beispiele
= 2 # Zuweisung x
== 2 # Vergleich x
True
> 2 x
False
< 10 and x > 5 x
False
Die vorige Verknüpfung von zwei Vergleichen kann in Python kürzer dargestellt werden:
5 < x < 10
False
< 10 or x > 5 # macht das Sinn? x
True
= 2 y
== y # Werte vergleichen x
True
is y # Objekte vergleichen x
True
Wir können mit der Funktion id
die IDs der Objekte x
und y
bestimmen:
id(x)
4467376400
id(y)
4467376400
Man erkennt, dass beide Objekte x
und y
dieselbe ID haben. Dies bedeutet, dass das zugrundeliegende Objekt 2
ein und dasselbe Objekt ist und lediglich zwei Namen x
und y
hat.
Die tatsächlichen IDs von Objekten sind ein Implementierungsdetail von Python, d.h. es ist nicht wichtig welche Zahl hier zurückgegeben wird. Dennoch kann man diese IDs verwenden, um zwei Objekte auf Gleichheit zu überprüfen, denn nur dann haben beide Objekte dieselbe ID.
Ein weiteres Implementierungsdetail von Python ist, dass es kleine Ganzzahlen genau ein Mal gibt – d.h. wenn man zwei oder mehrere Objekte mit kleinen Zahlen anlegt (wie im vorigen Beispiel), dann wird immer nur ein Objekt erzeugt. Dies dient der Beschleunigung bzw. effizienteren Speichernutzung. Bei großen Zahlen ist das allerdings nicht mehr so:
= 500
a a
500
id(a)
4849825328
= a # b und a sind Namen für dasselbe Objekt
b b
500
id(b)
4849825328
= 500 # b ist nun ein unterschiedliches Objekt 500
b b
500
id(b)
4849826288
== b # Werte gleich? a
True
is b # Objekte gleich? a
False
Das folgende Beispiel veranschaulicht, dass es einen Unterschied zwischen Ganzzahlen (int
) und Kommazahlen (float
) gibt, obwohl deren Werte (zumindest mathematisch) gleich sein können.
= 12
c = 12.0 d
== d c
True
is d c
False
Man sollte Kommazahlen aber ohnehin nie auf Gleichheit überprüfen, da diese aufgrund der begrenzten Genauigkeit nicht exakt repräsentiert werden können (siehe dazu auch die Beispiele aus der ersten Einheit 4 / 0.4
, 4 // 0.4
und 4 % 0.4
).
Ein weiteres Beispiel, welches mathematisch korrekt ist, aber dennoch False
ergibt, lautet:
0.1 + 0.1 + 0.1 == 0.3
False
Möchte man so einen Vergleich durchführen, ist es sinnvoller zu fragen, ob der Unterschied zwischen den beiden Werten einen bestimmten (kleinen) Betrag (z.B. \(10^{-15}\)) nicht überschreitet – dann kann man davon ausgehen dass die Werte numerisch praktisch identisch sind.
0.1 + 0.1 + 0.1) - 0.3 < 1e-15 (
True
Das math
-Modul liefert die Funktion isclose
mit, welche genau diese Überprüfung durchführt:
import math
0.1 + 0.1 + 0.1, 0.3) math.isclose(
True
In Python kann man Zahlen auch in der sogenannten wissenschaftlichen Notation anschreiben. Hier verwendet man eine Darstellung mit Zehnerpotenzen, die man in Python mit e
eingeben kann – e
kann man als “mal zehn hoch” lesen.
1e0 # 1 mal 10 hoch 0
1.0
4e0 # 4 mal 10 hoch 0
4.0
1e1 # 1 mal 10 hoch 1
10.0
3.5e2 # 3.5 mal 10 hoch 2
350.0
1e-2 # 1 mal 10 hoch -2
0.01
1e-15 # 1 mal 10 hoch -15 = 0.000000000000001
1e-15
Bedingungen
Eine Bedingung wird in Python mit den Schlüsselwörtern if
, elif
und else
realisiert. Dabei wird überprüft, ob ein Ausdruck wahr (True
) oder falsch (False
) ist. Nur falls dieser Ausdruck True
ist, wird der nachfolgende eingerückte Codeblock ausgeführt, sonst nicht. Die grundsätzliche Struktur sieht wie folgt aus:
if <statement is True>:
<do something>
...elif <statement is True>: # optional
<do something>
...elif <statement is True>: # optional
<do something>
...else: # optional
<do something>
...
Der Aufbau einer Bedingung ist also im Prinzip derselbe wie bei einer Funktion. Zuerst gibt es den Kopf, welcher mit dem Keyword if
eingeleitet wird. Danach folgt ein logischer Ausdruck (meist ein Vergleich), und zum Schluss wird die Kopfzeile mit einem :
abgeschlossen. Der darauf folgende eingerückte Code wird nur ausgeführt, wenn der logische Ausdruck True
ergibt – wenn das nicht der Fall ist, wird der gesamte eingerückte Codeblock übersprungen.
Nur wenn der erste Ausdruck True
ist, wird also der eingerückte Code ausgeführt. Danach wird der gesamte restliche if
/elif
/else
-Block verlassen, es wird also kein weiterer Code mehr ausgeführt. Wenn der erste Ausdruck False
ist, wird der zugehörige Codeblock nicht ausgeführt und es wird zum nächsten elif
-Ausdruck gesprungen (falls vorhanden). Hier wird dann ein weiterer logischer Ausdruck ausgewertet, und falls dieser True
ist, wird der dazugehörige eingerückte Codeblock ausgeführt. Falls kein logischer Ausdruck in den elif
-Zweigen True
ist, wird schließlich der Codeblock im else
-Zweig ausgeführt (falls vorhanden).
In einer Bedingung wird maximal ein Codeblock ausgeführt, nämlich der erste, bei dem der logische Ausdruck True
ist. Deshalb ist auch die Reihenfolge der einzelnen Zweige von Bedeutung.
Beispiele
Beginnen wir mit einem einfachen Beispiel, bei dem nur ein if
-Zweig vorhanden ist:
= 2
a
if a > 0:
print("a is a positive number")
print("this is good to know")
a is a positive number
this is good to know
Ergibt der Vergleich a > 0
also False
, wird der eingerückte Code nicht ausgeführt:
= -10
a
if a > 0:
print("a is a positive number")
print("this is good to know")
Man kann optional einen else
-Zweig verwenden, der immer dann ausgeführt wird, wenn alle vorhergehenden Zweige False
waren:
= 0
a
if a > 0:
print("a is a positive number")
print("this is good to know")
else:
print("a is either 0 or a negative number")
a is either 0 or a negative number
Nun kann man noch mit elif
beliebig viele weitere Zweige einbauen:
= 0
a
if a > 0:
print("a is a positive number")
print("this is good to know")
elif a < 0:
print("a is a negative number")
else:
print("a is 0")
a is 0
Sobald ein Ausdruck in einem if
-Block True
ist, wird dieser ausgeführt und der gesamte Block wird verlassen. Es werden also keine weiteren Vergleiche mehr durchgeführt.
= 2
x
if x == 2:
print("x is", x)
elif x > 0:
print("x is greater than 0")
elif x < 0:
print("x is negative")
else:
print("x is 0")
x is 2
Dementsprechend kann auch die Reihenfolge der einzelnen Zweige von Bedeutung sein:
= 4
a
if a > 5:
print("One")
elif a < 10:
print("Two")
elif a == 4:
print("Three")
else:
print("Four")
Two
= 4
a
if a > 5:
print("One")
elif a == 4:
print("Three")
elif a < 10:
print("Two")
else:
print("Four")
Three
Selbstverständlich kann man Vergleiche nicht nur mit Zahlen durchführen:
= "Python"
s
if s == "Python":
print("Way to go!")
elif s == "R":
print("Statistics")
else:
print("Unknown")
Way to go!
= "R"
s
if s == "Python":
print("Way to go!")
elif s == "R":
print("Statistics")
else:
print("Unknown")
Statistics
for-Schleifen
Um Befehle zu wiederholen, gibt es die Möglichkeit, Schleifen zu verwenden. Eine häufig verwendete Schleife ist die sogenannte for
-Schleife. Als einfaches Beispiel ersetzen wir folgenden repetitiven Code durch eine Schleife:
print("Hallo")
print("Hallo")
print("Hallo")
Hallo
Hallo
Hallo
for i in range(3):
print("Hallo")
Hallo
Hallo
Hallo
Die sogenannte Schleifenvariable i
nimmt hier in den drei Durchläufen drei verschiedene Werte 0, 1 und 2 an – dies sind nämlich genau die Werte, die die Funktion range
zurückgibt. Der Name der Schleifenvariable kann beliebig gewählt werden, oft wird für kurze Schleifen einfach i
verwendet. Dieser Name bleibt nach dem Ausführen der Schleife übrigens bestehen, er unterscheidet sich nicht von anderen durch Zuweisung entstandene Namen.
Überlegen Sie, welchen Wert i
am Ende des folgenden Beispiels hat:
= 800
i for i in range(3):
print("Hello")
Die Funktion range
wird mit range(start, stop, step)
aufgerufen (der Hilfetext verrät dazu mehr Details) und erzeugt eine Sequenz, welche aus ganzen Zahlen besteht die von start
(optional) bis stop
in der Schrittweite step
(optional) läuft. Wenn man sich die einzelnen Elemente in einem range
-Objekt ansehen will, muss man dieses zuerst in eine Liste umwandeln:
= range(10)
x x
range(0, 10)
list(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Grundsätzlich beginnt Python mit 0 zu zählen, d.h. auch range
beginnt standardmäßig bei 0. Die letzte Zahl ist nicht mehr Teil der Sequenz, da man so einfach die Anzahl der erzeugten Elemente sehen kann (im Beispiel oben sieht man, dass range(10)
aus 10 Elementen besteht).
In Python iteriert eine for
-Schleife über alle Elemente einer Sequenz (d.h. alle Datentypen, die aus mehreren Elementen bestehen und iterierbar sind, wie z.B. Strings oder Listen). Im folgenden Beispiel iteriert die Schleife über einen String, d.h. bei jedem Schleifendurchlauf werden die einzelnen Elemente (Buchstaben) eines Strings der Schleifenvariable s
zugewiesen:
for s in "String":
print(s)
S
t
r
i
n
g
Dies funktioniert genauso mit Listen, da diese auch zur Gruppe der Sequenzdatentypen gehören und mehrere Elemente beinhalten können:
= ["Hello", "world!", "I", "love", "Python!"]
a
for element in a:
print(element)
Hello
world!
I
love
Python!
Der Befehl break
bricht die aktuelle Schleife ab (d.h. er bricht daraus aus). Python springt ans Ende der Schleife und macht von hier mit der Ausführung der noch folgenden Befehlszeilen weiter.
= 0
i
for c in "Suchstring":
if c == "u":
break # beende Schleife sofort
+= 1 # Abkürzung für i = i + 1
i
print(i)
1
Im Beispiel oben wird ein bestimmtes Zeichen in einem String gesucht, dessen Position dann in i
abzulesen ist.
Der Befehl continue
geht sofort zur nächsten Iteration der Schleife (überspringt also den restlichen Code der Schleife, der noch danach folgt).
for num in range(2, 10):
if num % 2 == 0: # gerade Zahl?
print("Found an even number", num)
continue # überspringe alle restlichen Zeilen innerhalb der Schleife
print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
Details zu Strings und Listen folgen in den nächsten Einheiten.
while-Schleifen
Im Gegensatz zu for
-Schleifen sind while
-Schleifen gut geeignet, wenn nicht im Vorhinein klar ist, wie oft die Schleife durchlaufen werden soll. Im folgenden Beispiel wird eine Endlosschleife verwendet (while True
ist immer True
). Zum Verlassen dieser Endlosschleife wird dann aber auf break
zurückgegriffen. Die Funktion input
wird verwendet, um Tastatureingaben vom Benutzer abzufragen – die eingegebenen Zeichen werden von der Funktion als String zurückgegeben.
while True:
= input("> (enter 'q' to quit) ")
line if line == "q":
break
Ein weiteres Beispiel einer while
-Schleife zeigt das folgende Zahlenratespiel:
= 23 # diese Zahl soll erraten werden
number
while True:
= int(input("Enter an integer: "))
guess if guess == number:
print("Congratulations, you guessed it.")
break
elif guess < number:
print("No, it is a little higher than that.")
else:
print("No, it is a little lower than that.")
Die Funktion int
wird hier verwendet, um die Benutzereingabe (ein String) in eine Ganzzahl zu konvertieren (z.B. wandelt int("7")
den String "7"
in eine Zahl 7
um).
Übungen
Übung 1
Schreiben Sie folgendes Programm:
- Lesen Sie zuerst mit der Funktion
input
zwei Zahlen ein (weisen Sie diesen beiden Zahlen die Namenx
undy
zu). Beachten Sie, dassinput
Strings zurückliefert und dass Sie diese mit der Funktionint
in Ganzzahlen umwandeln können. - Wenn die Summe der beiden Zahlen größer als 50 ist, geben Sie
x + y > 50
am Bildschirm aus. - Wenn die Summe der beiden Zahlen kleiner als 50 ist, geben Sie
x + y < 50
am Bildschirm aus. - Ansonsten geben Sie
x + y == 50
aus.
Hinweise: Die Builtin-Funktion input
ermöglicht es, Eingaben von der Tastatur einzulesen. Der Benutzer kann beliebige Zeichen eingeben und mit der Eingabetaste bestätigen. Das Ergebnis der Eingabe wird dann von der Funktion zurückgegeben (d.h. man kann diesem Wert auch einen Namen zuweisen, z.B. x = input()
). Der Typ des Rückgabewerts ist immer str
.
Übung 2
Schreiben Sie eine Funktion is_odd
, welche einen Parameter hat und überprüft, ob das übergebene Argument gerade oder ungerade ist (das können Sie z.B. durch den Rest der Division durch 2 überprüfen). Falls das Argument ungerade ist, soll die Funktion True
mittels return
zurückgeben, sonst False
.
Übung 3
Gegeben ist eine Liste lst = ["I", "love", "Python"]
. Schreiben Sie eine for
-Schleife, welche die einzelnen Elemente in der Liste mit print
Zeile für Zeile am Bildschirm ausgibt.
Übung 4
Gegeben ist wieder eine Liste lst = ["I", "love", "Python"]
. Schreiben Sie eine for
-Schleife, welche über die einzelnen Elemente in der Liste iteriert. Eine zweite (geschachtelte) for
-Schleife soll dann über jeden Buchstaben der einzelnen Strings iterieren und jeden Buchstaben einzeln gefolgt vom Zeichen -
ausgeben.
Hinweis: Verwenden Sie die Funktion print
mit dem Argument end="-"
. Die Ausgabe soll wie folgt aussehen: I-l-o-v-e-P-y-t-h-o-n-
Übung 5
Schreiben Sie das folgende verschachtelte if
–else
-Konstrukt als if
–elif
–else
-Block.
if x > 0:
print("x is positive")
else:
if x < 0:
print("x is negative")
else:
print("x is equal to 0")
Überprüfen Sie Ihre Lösung mit den drei Werten x = 5
, x = -11
und x = 0
(d.h. bei beiden Varianten soll dasselbe Ergebnis herauskommen).