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.

Img 1

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:

Img 1

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.

Img 1

Die Variable \(\texttt{x}\) wird mit \(\texttt{x}=1\) initialisiert und die Variable \(\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\).

Img 1

Die Variable \(\texttt{x}\) wird nun mit \(\texttt{x}=-2\) initialisiert und die Variable \(\texttt{y}\) mit \(\texttt{y}=4\). Da jetzt \(\texttt{x} \le 0\) gilt, wird \(\texttt{y}\) nicht verändert und somit gilt \(\texttt{z} = \frac{4}{2} = 2\).

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

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.

Img 1
Img 1

Die Variable \(\texttt{x}\) wird mit \(\texttt{x}=1\) initialisiert und die Variable \(\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\).

Img 1

Die Variable \(\texttt{x}\) wird nun mit \(\texttt{x}=-2\) initialisiert und die Variable \(\texttt{y}\) mit \(\texttt{y}=4\). Da jetzt \(\texttt{x} \le 0\) gilt, wird \(\texttt{y}\) nun zu \(\texttt{y} = 2 \cdot 4 = 8\) und somit gilt \(\texttt{z} = \frac{8}{2} = 4\).

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

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.

Img 1

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.

Img 1

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\).

Img 1

Die Variable \(\texttt{x}\) wird nun mit \(\texttt{x}=-2\) initialisiert und \(\texttt{y}\) mit \(\texttt{y}=4\). Es gilt \(\texttt{x} \le 0\). Da jedoch \(\texttt{x} < - 1\), wird \(\texttt{y}\) nun zu \(\texttt{y} = 2 * 4 = 8\) und somit gilt \(\texttt{z} = \frac{8}{2} = 4\).

Img 1

Die Variable \(\texttt{x}\) wird nun mit \(\texttt{x}=-0.5\) initialisiert und \(\texttt{y}\) mit \(\texttt{y}=4\). Da jetzt sowohl \(\texttt{x} \le 0\) als auch \(\texttt{x} \ge -1 \) gilt, wird \(\texttt{y}\) nun zu \(\texttt{y} = 4 - 2 = 2\) und somit gilt \(\texttt{z} = \frac{2}{2} = 1\).

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

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

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:

\[\begin{split} \begin{array}{lrlrl} & \texttt{einkommen} & \le 12096 & \Rightarrow & \texttt{grenzsteuersatz} = 0, \\ 12096 < & \texttt{einkommen} & \le 17443 & \Rightarrow & \texttt{grenzsteuersatz}= 0.14, \\ & \texttt{einkommen} & \ge 17444 & \Rightarrow & \texttt{grenzsteuersatz}= 0.2397. \end{array} \end{split}\]

Erweitern Sie Ihren Code, so dass nun auch für Zone 3 der korrekte Grenzsteuersatz ausgegeben wird, sprich:

\[\begin{split} \begin{array}{lrlrl} & \texttt{einkommen} & \le 12096 & \Rightarrow & \texttt{grenzsteuersatz}= 0, \\ 12096 < & \texttt{einkommen} & \le 17443 & \Rightarrow & \texttt{grenzsteuersatz}= 0.14, \\ 17433 < & \texttt{einkommen} & \ge 68480 & \Rightarrow & \texttt{grenzsteuersatz}= 0.2397, \\ & \texttt{einkommen} & \ge 68481 & \Rightarrow & \texttt{grenzsteuersatz}= 0.42. \end{array} \end{split}\]

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))

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.

Hide 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()

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:

Img 1

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}\).

\[\begin{split} \begin{array}{rlrl} \texttt{tag_nr} & = 1 & \Rightarrow & \texttt{wochentag} = \text{Montag}, \\ \texttt{tag_nr} & = 2 & \Rightarrow & \texttt{wochentag} = \text{Dienstag}, \\ \texttt{tag_nr} & = 3 & \Rightarrow & \texttt{wochentag} = \text{Mittwoch}, \\ \texttt{tag_nr} & = 4 & \Rightarrow & \texttt{wochentag} = \text{Donnerstag}, \\ \texttt{tag_nr} & = 5 & \Rightarrow & \texttt{wochentag} = \text{Freitag}, \\ \texttt{tag_nr} & \in \{6, 7 \} & \Rightarrow & \texttt{wochentag} = \text{Wochenende}. \end{array} \end{split}\]

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)