Bedingte Anweisungen#
Das Konzept von bedingten Anweisungen begegnet uns im Alltag ständig. Zum Beispiel richten sich die Parkgebühren in einem Parkhaus meist nach der Parkdauer – es sei denn, Sie stehen länger als 24 Stunden im Parkhaus, dann zahlen Sie oft einen festen Tagestarif. Oder Sie planen einen Besuch in der Mannheimer Kunsthalle. Welches Ticket für Sie das günstigste ist, hängt von Ihrer Lebenssituation ab und ob Sie Ihren Besuch alleine oder als Gruppe planen. Vielleicht besuchen Sie die Kunsthalle aber auch am ersten Mittwoch im Monat nach 18 Uhr – dann ist der Eintritt kostenlos.
All diese Entscheidungen hängen von bestimmten Bedingungen ab. Genau solche Situationen lassen sich in der Programmierung mit bedingten Anweisungen abbilden. Diese erlauben es, je nach Situation unterschiedliche Anweisungen auszuführen.
In Python können solche Entscheidungsprozesse mit \(\texttt{if}\), \(\texttt{else}\) und \(\texttt{elif}\) umgesetzt werden. Für endlich viele, klar definierte Fälle (z.B. bei Menüauswahlen) gibt es zusätzlich die sogenannte switch-Anweisungen, welche in Python durch das Schlüsselwort \(\texttt{match}\) codiert ist.
Bemerkung
Mit bedingten Anweisungen lässt sich steuern, welcher Programmcode ausgeführt wird, je nachdem ob eine Bedingung wahr oder falsch ist.
if-elif-else-Anweisung#
Bevor Sie die vollständige Struktur von if-elif-else-Anweisungen kennenlernen, tasten wir uns schrittweise heran und beginnen zunächst mit einfachen if-Anweisungen.
if-Anweisung#
In Python hat ein if-Anweisung die folgende Syntax:
Auf das Schlüsselwort \(\texttt{if}\) folgt immer eine Bedingung, die entweder zu \(\texttt{True}\) oder \(\texttt{False}\) ausgewertet wird. Der Codeblock, der ausgeführt werden soll, wenn die Bedingung \(\texttt{condition}\) wahr ist, wird durch den Doppelpunkt \(\texttt{:}\) eingeleitet und durch ein Leerzeichen oder Tab unterhalb des Schlüsselwortes eingerückt. Der Code \(\texttt{another}\) \(\texttt{statement 1, 2}\) wird unabhängig vom Wahrheitsgehalt der Bedingung \(\texttt{condition}\) ausgeführt.
Aufgabe 1.1
Modifizieren Sie den Code so, dass der Variable \(\texttt{b}\) der Wert \(1\) zugewiesen wird, wenn \(\texttt{a}\) größer als \(1\) ist. Ändern Sie den Wert für \(\texttt{a}\) anschließend zu \(0\) und führen Sie den Code erneut aus.
import numpy as np
# falls Sie den Code für verschiedene Werte von a testen wollen bitte "del b" auskommentieren,
# damit b für jeden Durchlauf gelöscht wird.
# del b
a = 4
# Ihr Code
Hinweis
Verwenden Sie eine if-Anweisung. Die Bedingung dabei ist, dass wenn \(\texttt{a} > 1\) gilt, soll der Code \(\texttt{b} = 1\) ausgeführt werden.
if condition:
code
Lösung
if a > 1:
b = 1
if-else-Anweisung#
Oft möchte man auch festlegen, welcher Code ausgeführt werden soll, wenn die Bedingung nicht erfüllt ist. Für diesen Zweck stellt Python das Schlüsselwort \(\texttt{else}\) zur Verfügung. Es ermöglicht, einen alternativen Codeblock zu definieren, der ausgeführt wird, wenn die zugehörige if-Bedingung verletzt ist.
Aufgabe 1.2
Passen Sie Ihren Code von Aufgabe 1.1 so an, dass wenn die Bedingung \(\texttt{a} > 1\) nicht erfüllt ist, dann wird \(\texttt{b}\) auf \(0\) gesetzt. Probieren Sie anschließend den Code erneut für den Wert \(\texttt{a} =0\).
a = 4 # um Code zu testen
# Ihr Code
Lösung
if a > 1:
b = 1
else:
b = 0
if-elif-else-Anweisung#
Wenn die Bedingung nach der if-Anweisung als falsch bewertet wird, kann mit \(\texttt{elif}\) (kurz für \(\texttt{else if}\)) eine weitere Bedingung geprüft werden. Es können beliebig viele elif-Blöcke ergänzt werden. Wenn keine der Bedingungen zutrifft, wird der else-Block ausgeführt. Auf diese Weise lässt sich steuern, welcher Code abhängig von unterschiedlichen Situationen ausgeführt wird.
Achtung
Sobald eine der Bedingungen in einem if-elif-else Statment erfüllt ist, wird nur der zugehörige Codeblock ausgeführt. Alle nachfolgenden Bedingungen werden ignoriert, selbst wenn sie ebenfalls wahr sind.
Die Variable \(\texttt{x}\) wird mit \(\texttt{x}=1\) initialisiert und \(\texttt{y}\) mit \(\texttt{y}=4\). Da \(\texttt{x} > 0\) gilt, wird \(\texttt{y}\) zu \(\texttt{y} = 4 + 3 = 7\) und somit gilt \(\texttt{z} = \frac{7}{2} = 3.5\).
Aufgabe 1.3
Passen Sie Ihren Code von Aufgabe 1.1 so an, dass wenn die Bedingung \(\texttt{a} > 1\) nicht erfüllt ist, aber \(\texttt{a} < 0\) dann wird \(\texttt{b}\) auf \(0\) gesetzt. Ansonsten wird \(\texttt{b}\) auf \(\frac{\texttt{a}}{2}\) gesetzt. Testen Sie ihren Code für verschiedene Werte von \(\texttt{a}\), um sicherzustellen dass alle Bedingungen richtig erfasst werden.
a = 4 # um Code zu testen
# Ihr Code
Lösung
if a > 1:
b = 1
elif a < 0:
b = 0
else:
b = a / 2
Aufgabe 1.4
Erweitern Sie Ihren Code von Aufgabe 1.1, sodass wenn \(\texttt{a} > 1\) und zusätzlich \(\texttt{a} < 4\) erfüllt ist, dann wird \(\texttt{b}\) auf \(4\) gesetzt. Gilt \(\texttt{a} \geq 4\), wird \(\texttt{b}\) auf \(1\) gesetzt und ansonsten auf \(-2\). Testen Sie ihren Code für verschiedene Werte von \(\texttt{a}\), um sicherzustellen dass alle Bedingungen richtig erfasst werden.
a = 4 # um Code zu testen
# Ihr Code
Hinweis
Verwenden Sie entweder eine verschachteltes if-else-Anweisung oder eine einfache if-elif-else-Anweisung. Das logische und ist in Python durch \(\texttt{&}\) gegeben.
Lösung
# Option 1
if a > 1:
if a < 4:
b = 4
else:
b = 1
else:
b = -2
# Option 2
if 1 < a & a < 4:
b = 4
elif a >= 4:
b = 1
else:
b = -2
Beispiel - Einkommenssteuer#
In Deutschland ist die Einkommensteuer progressiv, das heißt: Je mehr Sie verdienen, desto höher ist der Steuersatz. Im Folgenden betrachten wir den Grezsteuersatz abhängig vom Einkommen. Diese sind in der Tabelle zusammengefasst.
Zone |
Einkommen (in Euro) |
Grenzsteuersatz |
|---|---|---|
0 |
\(0\) bis \(12096\) |
\(0 \%\) |
1 |
\(12097\) bis \(17443\) |
\(14 \%\) |
2 |
\(17444\) bis \(68480\) |
\(23.97 \%\) |
3 |
\(68481\) bis \(277825\) |
\(42 \%\) |
4 |
ab \(277826\) |
\(45 \%\) |
Je nach Höhe Ihres Einkommens soll Ihr Programm nun automatisch den passenden Grenzteuersatz bestimmen.
Passen Sie den Code so an, dass wenn \(\texttt{einkommen}\) kleiner gleich als \(12096\) ist, dann wird \(\texttt{grenzsteuersatz}\) auf \(0\) gesetzt und andernfalls auf \(0.14\).
Erweitern Sie Ihren Code, so dass nun auch für Zone 2 der korrekte Grenzsteuersatz ausgegeben wird, sprich:
Erweitern Sie Ihren Code, so dass nun auch für Zone 3 der korrekte Grenzsteuersatz ausgegeben wird, sprich:
Erweitern Sie Ihren Code, so dass für alle Zonen der korrekte Grenzsteuersatz bestimmt wird.
import numpy as np
# zufälliges Einkommen generieren
einkommen = np.random.randint(0, 300000)
# Ihr Code
grenzsteuersatz = 0
grenzsteuersatz = 0.14
print("Einkommen: " + str(einkommen))
print("Grenzsteuersatz: " + str(grenzsteuersatz))
Hinweis
Verwenden Sie eine if-else-Anweisung der Form:
if a <= b:
c = s1
else
c = s2
Lösung A2.1
if einkommen <= 12096:
grenzsteuersatz = 0
else:
grenzsteuersatz = 0.14
print("Einkommen: " + str(einkommen))
print("Grenzsteuersatz: " + str(grenzsteuersatz))
Lösung A2.2
if einkommen <= 12096:
grenzsteuersatz = 0
elif einkommen <= 17443:
grenzsteuersatz = 0.14
else:
grenzsteuersatz = 0.2397
print("Einkommen: " + str(einkommen))
print("Grenzsteuersatz: " + str(grenzsteuersatz))
Lösung A2.3
if einkommen <= 12096:
grenzsteuersatz = 0
elif einkommen <= 17443:
grenzsteuersatz = 0.14
elif einkommen <= 68480:
grenzsteuersatz = 0.2397
else:
grenzsteuersatz = 0.42
print("Einkommen: " + str(einkommen))
print("Grenzsteuersatz: " + str(grenzsteuersatz))
Lösung A2.4
if einkommen <= 12096:
grenzsteuersatz = 0
elif einkommen <= 17443:
grenzsteuersatz = 0.14
elif einkommen <= 68480:
grenzsteuersatz = 0.2397
elif einkommen <= 277825:
grenzsteuersatz = 0.42
else:
grenzsteuersatz = 0.45
print("Einkommen: " + str(einkommen))
print("Grenzsteuersatz: " + str(grenzsteuersatz))
Beispiel - Erneuerbare Energien#
An dieser Stellen wollen wir nochmal den Anteil erneuerbarer Energien am Gesamtenergieverbrauch für verschiedene Länder graphisch darstellen. Den Datensatz haben Sie bereits im Kapitel zu for-Schleifen kennengelernt. Die gegebenen Daten sind:
\(\texttt{years}\) - NumPy-Array, welches die Jahre 2005 bis 2023 enthält
\(\texttt{countries}\) - NumPy-Array, welche die verschiedenen Länder enthält
\(\texttt{data}\) - NumPy-Array, welches die Daten zum Anteil erneuerbarer Energien der Länder enthält. Eine Zeile entspricht einem Jahr, eine Spalte einem Land.
Show code cell content
import numpy as np
import pandas as pd
# Datenquelle
url = "https://raw.githubusercontent.com/owid/energy-data/master/owid-energy-data.csv"
df = pd.read_csv(url)
# Auswahlparameter
countries = [
"Germany",
"United States",
"China",
"Norway",
"Brazil",
"Italy",
"France",
"Netherlands",
"Sweden",
"Switzerland",
"Belgium",
]
variable = "renewables_share_energy"
# Daten filtern und auf relevante Spalten reduzieren
df_filtered = df[(df["country"].isin(countries)) & (df["year"] >= 2005)]
df_pivot = df_filtered.pivot(index="year", columns="country", values=variable)
# Nur vollständige Zeilen und relevante Länder
df_selected = df_pivot[countries].dropna()
# Jahr als eigene Spalte
df_selected_with_years = df_selected.copy()
df_selected_with_years.insert(0, "year", df_selected.index)
# Umwandeln in NumPy-Array
years = df_selected_with_years.to_numpy()[:, 0]
data = df_selected_with_years.to_numpy()[:, 1:]
data[np.random.randint(0, np.size(years)), 3] = np.nan
countries = np.array(countries)
Basierend auf den Eingaben der Nutzerin wollen wir ihr eine unmittelbare Rückmeldung anzeigen. In dieser Übung sollen Sie die Nutzerin informieren, falls keine Daten für das gewählte Land vorliegen oder Werte im Datensatz des Landes fehlen.
Fehlende Datensätze für Länder
Um zu überprüfen, ob ein bestimmtes Element – in diesem Fall ein Land – überhaupt im Datensatz enthalten ist, können Sie den Operator \(\texttt{in}\) verwenden. Damit lässt sich feststellen, ob ein gesuchtes Element in einem Array vorkommt. Der Ausdruck \(\texttt{x in array}\) liefert einen Wahrheitswert (\(\texttt{True}\) oder \(\texttt{False}\)) und gibt somit an, ob das Element enthalten ist, aber nicht wo.
obstkorb = np.array(["Apfel", "Banane", "Kiwi"])
print("Ananas" in obstkorb)
print("Kiwi" in obstkorb)
Analog können Sie mit dem Operator \(\texttt{not in}\) prüfen ob ein Element nicht in einem Array enthalten ist.
Fehlende Werte in den Daten eines Landes
Im Kontext der logischen Indizierung haben Sie bereits gelernt, wie man prüft, ob Elemente eines Arrays einen bestimmten Wert besitzen – zum Beispiel mit \(\texttt{x == 5}\). Dies liefert ein logisches Array, das an jeder Stelle angibt, ob die Bedingung erfüllt ist oder nicht. Für fehlende Werte (\(\texttt{nan}\)) funktioniert ein direkter Vergleich wie \(\texttt{x == np.nan}\) oder \(\texttt{np.nan in x}\) jedoch nicht – dieser liefert immer \(\texttt{False}\). Verwenden Sie stattdessen die Funktion \(\texttt{np.isnan()}\), um ein logisches Array zu erzeugen, das markiert, welche Einträge \(\texttt{nan}\) sind.
Wenn Sie prüfen möchten, ob mindestens ein Eintrag in einem Array \(\texttt{nan}\) ist, können Sie zusätzlich die Funktion \(\texttt{any()}\) einsetzen. Diese gibt \(\texttt{True}\) zurück, sobald mindestens ein \(\texttt{True}\)-Wert im Array enthalten ist.
Prüfen Sie ob für das Land \(\texttt{ctry}\) im Datensatz vorhanden ist, das heißt ob \(\texttt{ctry}\) ein Element von \(\texttt{countries}\) ist. Falls nicht, geben Sie eine Fehlermeldung mit \(\texttt{raise Exception(errMsg)}\) aus. Testen Sie Ihren Code für \(\texttt{"Canada"}\).
Prüfen Sie, ob Werte in den Daten zum Anteil erneuerbarer Energien eines Landes fehlen (\(\texttt{nan}\)-Werte). Falls ja, soll die Warnmeldung \(\texttt{warnMsg}\) als Warnung mit \(\texttt{print()}\) ausgegeben werden. Testen Sie Ihren Code für \(\texttt{ctry = "Norway"}\).
import numpy as np
import matplotlib.pyplot as plt
ctry = "Canada"
warnMsg = "Es fehlen einzelne Datenpunkte für " + ctry + "."
errMsg = "Daten für " + ctry + " fehlen!"
# Ihr Code
k = list(countries).index(ctry)
ctry_data = data[:, k]
plt.figure()
plt.plot(years, ctry_data)
plt.xlabel("Jahre")
plt.ylabel("Anteil erneuerbarer Energien (%)")
plt.show()
Hinweis A3.1
Verwenden Sie eine if-else-Anweisung und prüfen Sie ob \(\texttt{ctry}\) (k)ein Element von \(\texttt{countries}\) ist. \(\texttt{raise Exception()}\) führt automatisch zum Programmabbruch.
if condition:
code
else:
raise Exception(errMsg)
Lösung A3.1
import numpy as np
import matplotlib.pyplot as plt
ctry = "Canada"
errMsg = "Daten für " + ctry + " fehlen!"
warnMsg = "Es fehlen einzelne Datenpunkte für " + ctry + "."
if ctry in countries:
k = countries.index(my_ctry)
ctry_data = data[:, k]
plt.figure()
plt.plot(years, ctry_data)
plt.xlabel("Jahre")
plt.ylabel("Anteil erneuerbarer Energien (%)")
plt.show()
else:
raise Exception(errMsg)
Lösung A3.2
import numpy as np
import matplotlib.pyplot as plt
ctry = "Norway"
warnMsg = "Es fehlen einzelne Datenpunkte für " + ctry + "."
errMsg = "Daten für " + ctry + " fehlen!"
if ctry in countries:
k = list(countries).index(ctry)
ctry_data = data[:, k]
if(any(np.isnan(ctry_data))):
print(warnMsg)
plt.figure()
plt.plot(years, ctry_data)
plt.xlabel("Jahre")
plt.ylabel("Anteil erneuerbarer Energien (%)")
plt.show()
else:
raise Exception(errMsg)
Exkurs: Vorzeitiges Beenden einer Schleife über if-Anweisungen#
Neben dem automatischen Beenden einer while-Schleife durch das Erreichen der Abbruchbedingung gibt es die Möglichkeiten eine Schleife mit if-Anweisungen gezielt vorzeitig zu verlassen oder zu beeinflussen. Dazu zählen insbesondere die Anweisungen \(\texttt{break}\), \(\texttt{return}\) und \(\texttt{continue}\). Sie bieten eine individuelle Kontrolle über den Schleifenablauf.
Mit \(\texttt{break}\) kann die Schleife sofort beendet werden, unabhängig davon, ob die Bedingung noch erfüllt ist.
\(\texttt{return}\) verlässt die aktuelle Funktion (und damit auch eine eventuelle Schleife darin) direkt und gibt optional einen Wert zurück.
\(\texttt{continue}\) überspringt den Rest des aktuellen Schleifendurchlaufs und beginnt direkt mit der nächsten Iteration.
Solche Mechanismen können über if-Anweisungen integriert werden und sind besonders nützlich, wenn Sonderfälle behandelt werden müssen.
Wir betrachten folgendes Beispiel: Ein Spieler würfelt so lange, bis die Summe aller Augenzahlen den Wert 21 überschreitet. Falls er zwischendurch eine 6 würfelt, endet das Spiel sofort, und er bekommt 21 Punkte. Ansonsten erhält er als Punktzahl die aktuelle Summe minus 21.
Der Sonderfall, dass das Spiel sofort endet sobald eine 6 gewürfelt wird, kann mit Hilfe des Befehls \(\texttt{break}\) und einer if-Anweisung erzielt werden. Der folgende Code simuliert genau dieses Beispiel. Mit \(\texttt{np.random.randint(1,7)}\) wird eine zufällige ganze Zahl zwischen \(1\) und \(6\) generiert. Diese Ausdruck simuliert demnach den Würfelwurf.
kumulative_augensumme = 0
while kumulative_augensumme <= 21:
wurf_aug = np.random.randint(1, 7)
print("Wurf: " + str(wurf_aug))
if wurf_aug == 6:
punktzahl = 21
break
kumulative_augensumme = kumulative_augensumme + wurf_aug
else:
# Diese Zeile wird nur ausgeführt, wenn break nicht eintritt
punktzahl = kumulative_augensumme - 21
print("Punktzahl:" + str(punktzahl))
Alternativ kann das Würfelspiel über eine Funktion und der Sonderfall mit Hilfe des Befehls \(\texttt{return}\) und einer if-Anweisung simuliert werden.
def wuerfelspiel():
kumulative_augensumme = 0
while kumulative_augensumme <= 21:
wurf_aug = np.random.randint(1, 7)
print("Wurf: " + str(wurf_aug))
if wurf_aug == 6:
return 21
kumulative_augensumme = kumulative_augensumme + wurf_aug
return kumulative_augensumme - 21
# Spiel starten
punktzahl = wuerfelspiel()
print("Punktzahl:" + str(punktzahl))
match-Anweisung#
Eine match-Anweisung erlaubt es, eine Variable oder einen Ausdruck mit mehreren vordefinierten Fällen zu vergleichen und je nach Ergebnis unterschiedlichen Code auszuführen. Es ist besonders dann hilfreich, wenn man einen Ausdruck gegen endlich viele Werte prüfen möchte. In anderen Programmiersprachen wie C, C++ oder Matlab spricht man stattdessen von switch-Anweisungen. Diese werden, anders als in Python, mit dem Schlüsselwort \(\texttt{switch}\) eingeleitet. In Syntax in Python ist wie folgt:
Auf das Schlüsselwort \(\texttt{match}\) folgt der Ausdruck \(\texttt{var}\), dessen Wert mit den definierten Fällen verglichen wird. Die sogenannte match-Fälle werden dann von oben nach unten geprüft. Das heißt, es wird überprüft ob, der Wert des Ausdrucks \(\texttt{var}\) und der Wert des Falls gleich sind. Ist dies der Fall, wird der zugehörige Codeblock ausgeführt.
Falls keiner der Fälle zutrifft, wird der default-Fall ausgeführt. Dieser wird durch einen Unterstrich (\(\texttt{_}\)) gekennzeichnet und entspricht dem „sonst“-Fall bei mathematischen Fallunterscheidungen. Wichtig: Der default-Fall (\(\texttt{_}\)) muss stets als letzter Fall angegeben werden, da Python die Fälle von oben nach unten prüft und den ersten passenden Fall ausführt – alle weiteren werden dann ignoriert.
Das match-Anweisung lässt sich äquivalent als eine if-elif-else-Anweisung schreiben. Es bietet jedoch eine kompaktere und besser lesbare Struktur, besonders wenn viele verschiedene Fälle unterschieden werden müssen.
match x:
case 1:
print("x = 1")
case 2:
print("x = 2")
case _:
print("x ist weder 1 noch 2.")
if x == 1:
print("x = 1")
elif x == 2:
print("x = 2")
else:
print("x ist weder 1 noch 2.")
Aufgabe 4.1
Überprüfen Sie welchen Wert die Variable \(\texttt{tag_nr}\) hat und speichern Sie entsprechend den korrekten Wochentag in der Variable \(\texttt{wochentag}\).
Ergänzen Sie dafür den nachfolgenden Code.
tag_nr = np.random.randint(1, 8)
# Ihr Code
wochentag = "Montag"
wochentag = "Dienstag"
#Ihr Code
print("Es ist der " + str(tag_nr) + ". Tag der Woche. Also ist " + wochentag)
Hinweis
Verwenden Sie eine match-Anweisung, wobei Sie \(\texttt{var}\) durch die Variable \(\texttt{tag_nr}\) ersetzen.
Lösung
match tag_nr:
case 1:
wochentag = "Montag"
case 2:
wochentag = "Dienstag"
case 3:
wochentag = "Mittwoch"
case 4:
wochentag = "Donnerstag"
case 5:
wochentag = "Freitag"
case _:
wochentag = "Wochenende"
print("Es ist der" + str(tag_nr) + ". Tag der Woche. Also ist " + wochentag)