9. Bedingungen mit boolescher Logik kombinieren
In Kapitel 7 haben wir boolesche Werte und einfache Bedingungen mithilfe von Vergleichsoperatoren kennengelernt. In Kapitel 8 haben wir diese Bedingungen verwendet, um mit if-Anweisungen Entscheidungen zu treffen. Aber Programme aus der Praxis müssen oft mehrere Bedingungen gleichzeitig prüfen. Sollten wir Zugriff gewähren, wenn der Benutzer das richtige Passwort hat und angemeldet ist? Sollten wir eine Warnung anzeigen, wenn die Temperatur zu heiß oder zu kalt ist? Sollten wir fortfahren, wenn die Datei nicht leer ist?
Python stellt drei logische Operatoren bereit, mit denen wir boolesche Werte kombinieren und verändern können: and, or und not. Diese Operatoren sind die Bausteine, um komplexe Entscheidungslogik in Ihren Programmen auszudrücken.
9.1) Logische Operatoren and, or und not
Die drei logischen Operatoren arbeiten mit booleschen Werten (oder Werten, die als boolesch behandelt werden können), um neue boolesche Ergebnisse zu erzeugen.
9.1.1) Der and-Operator
Der and-Operator gibt True nur dann zurück, wenn beide Operanden wahr sind. Wenn einer der Operanden falsch ist, ist der gesamte Ausdruck falsch.
# Beide Bedingungen müssen wahr sein
age = 25
has_license = True
can_rent_car = age >= 21 and has_license
print(can_rent_car) # Output: True
# Wenn eine der Bedingungen falsch ist, ist das Ergebnis False
age = 18
can_rent_car = age >= 21 and has_license
print(can_rent_car) # Output: FalseStellen Sie sich and wie einen strengen Türsteher vor: alle Bedingungen müssen erfüllt sein, damit die gesamte Prüfung erfolgreich ist.
Wahrheitstabelle für and:
| Linker Operand | Rechter Operand | Ergebnis |
|---|---|---|
True | True | True |
True | False | False |
False | True | False |
False | False | False |
9.1.2) Der or-Operator
Der or-Operator gibt True zurück, wenn mindestens ein Operand wahr ist. Er gibt nur dann False zurück, wenn beide Operanden falsch sind.
# Mindestens eine Bedingung muss wahr sein
is_weekend = True
is_holiday = False
can_sleep_in = is_weekend or is_holiday
print(can_sleep_in) # Output: True
# Beide Bedingungen falsch
is_weekend = False
is_holiday = False
can_sleep_in = is_weekend or is_holiday
print(can_sleep_in) # Output: FalseStellen Sie sich or wie einen nachsichtigen Türsteher vor: Sie müssen nur eine Bedingung erfüllen, um durchzukommen.
Wahrheitstabelle für or:
| Linker Operand | Rechter Operand | Ergebnis |
|---|---|---|
True | True | True |
True | False | True |
False | True | True |
False | False | False |
Hier ist ein praktisches Beispiel für ein System zur Rabattberechtigung:
# Kunde bekommt Rabatt, wenn er Student ODER Rentner ist
age = 68
is_student = False
gets_discount = is_student or age >= 65
print(f"Eligible for discount: {gets_discount}") # Output: Eligible for discount: True
# Ein weiterer Kunde
age = 30
is_student = False
gets_discount = is_student or age >= 65
print(f"Eligible for discount: {gets_discount}") # Output: Eligible for discount: FalseDer erste Kunde qualifiziert sich, weil er eines der Kriterien erfüllt (Rentner), auch wenn er kein Student ist.
9.1.3) Der not-Operator
Der not-Operator ist ein unärer Operator (er arbeitet mit einem einzelnen Operanden), der einen booleschen Wert umkehrt. Er macht aus True ein False und aus False ein True.
is_raining = False
is_sunny = not is_raining
print(is_sunny) # Output: True
is_raining = True
is_sunny = not is_raining
print(is_sunny) # Output: FalseWahrheitstabelle für not:
| Operand | Ergebnis |
|---|---|
True | False |
False | True |
Der not-Operator ist besonders nützlich, wenn Sie das Gegenteil einer Bedingung prüfen möchten:
# Prüfen, ob eine Datei NICHT leer ist
file_size = 0
is_empty = file_size == 0
is_not_empty = not is_empty
print(f"File has content: {is_not_empty}") # Output: File has content: False
# Prüfen, ob der Benutzer NICHT eingeloggt ist
is_logged_in = False
needs_login_prompt = not is_logged_in
print(f"Show login prompt: {needs_login_prompt}") # Output: Show login prompt: True9.1.4) Mehrere logische Operatoren kombinieren
Sie können mehrere logische Operatoren in einem einzigen Ausdruck kombinieren, um anspruchsvollere Bedingungen zu erstellen:
# Online-Shop: kostenloser Versand, wenn Bestellung über 50 $ liegt ODER Kunde Premium-Mitglied ist
# UND Artikel auf Lager sind
order_total = 45.00
is_premium = True
in_stock = True
gets_free_shipping = (order_total >= 50 or is_premium) and in_stock
print(f"Free shipping: {gets_free_shipping}") # Output: Free shipping: TrueVerfolgen wir diese Auswertung:
order_total >= 50wird zuFalseausgewertet (45.00 ist nicht >= 50)is_premiumistTrueFalse or Truewird zuTrueausgewertetin_stockistTrueTrue and Truewird zuTrueausgewertet
Hier ist ein weiteres Beispiel mit Zugriffskontrolle:
# Benutzer kann auf Admin-Panel zugreifen, wenn er Admin ist
# UND (er im internen Netzwerk ist ODER VPN verwendet)
is_admin = True
on_internal_network = False
using_vpn = True
can_access_admin = is_admin and (on_internal_network or using_vpn)
print(f"Can access admin panel: {can_access_admin}") # Output: Can access admin panel: TrueBeachten Sie die Klammern um (on_internal_network or using_vpn). Diese sind wichtig, weil sie die Auswertungsreihenfolge steuern, genau wie Klammern in arithmetischen Ausdrücken.
9.2) Operatorpriorität in booleschen Ausdrücken (Reihenfolge: Not, And, Or)
Wenn Sie mehrere logische Operatoren ohne Klammern kombinieren, folgt Python bestimmten Prioritätsregeln, um die Auswertungsreihenfolge zu bestimmen. Wenn Sie diese Regeln verstehen, hilft Ihnen das, korrekte Bedingungen zu schreiben und subtile Bugs zu vermeiden.
9.2.1) Die Prioritätshierarchie
Python wertet logische Operatoren in dieser Reihenfolge aus (von höchster zu niedrigster Priorität):
not(höchste Priorität)and(mittlere Priorität)or(niedrigste Priorität)
Das bedeutet, not wird zuerst ausgewertet, dann and und schließlich or.
# Ohne Klammern bestimmt die Priorität die Reihenfolge
result = True or False and False
print(result) # Output: True
# So wertet Python das aus:
# Schritt 1: False and False → False (and hat höhere Priorität als or)
# Schritt 2: True or False → TrueSehen wir uns das Schritt für Schritt mit einem detaillierteren Beispiel an:
is_weekend = False
is_holiday = True
has_work = True
# Ausdruck: not has_work or is_weekend and is_holiday
free_time = not has_work or is_weekend and is_holiday
# Auswertungsreihenfolge:
# Schritt 1: not has_work → not True → False
# Schritt 2: is_weekend and is_holiday → False and True → False
# Schritt 3: False or False → False
print(f"Has free time: {free_time}") # Output: Has free time: False9.2.2) Klammern zur Klarheit verwenden
Selbst wenn Sie die Prioritätsregeln verstehen, machen Klammern Ihren Code klarer und verhindern Fehler. Klammern setzen die Standardpriorität außer Kraft und machen Ihre Absichten explizit.
# Mehrdeutig ohne Klammern
result = True or False and False
print(result) # Output: True
# Klar mit Klammern - was wollten wir wirklich ausdrücken?
result = (True or False) and False
print(result) # Output: False
result = True or (False and False)
print(result) # Output: TrueDiese beiden Ausdrücke liefern unterschiedliche Ergebnisse! Die Klammern verändern die Bedeutung vollständig.
9.2.3) Vergleichsoperatoren und logische Operatoren zusammen
Vergleichsoperatoren (wie <, >, ==, !=) haben eine höhere Priorität als logische Operatoren. Das bedeutet, Vergleiche werden vor logischen Operationen ausgewertet.
age = 25
income = 50000
# Keine Klammern um Vergleiche nötig
eligible = age >= 18 and income >= 30000
print(f"Eligible for loan: {eligible}") # Output: Eligible for loan: True
# Python wertet das so aus:
# Schritt 1: age >= 18 → True
# Schritt 2: income >= 30000 → True
# Schritt 3: True and True → True9.3) Short-Circuit-Auswertung
Python verwendet Short-Circuit-Auswertung bei der Auswertung boolescher Ausdrücke mit and und or. Das bedeutet, Python beendet die Auswertung, sobald das Endergebnis feststeht, und überspringt dabei möglicherweise die Auswertung späterer Operanden. Dieses Verhalten ist sowohl eine Performance-Optimierung als auch eine nützliche Programmiertechnik.
9.3.1) Wie and Short-Circuit verwendet
Beim and-Operator gilt: Wenn der linke Operand False ist, weiß Python, dass der gesamte Ausdruck False sein muss (weil beide Operanden wahr sein müssen, damit and True zurückgibt). Daher wertet Python den rechten Operanden überhaupt nicht aus.
# Einfache Demonstration
x = 5
result = x < 3 and x > 10
print(result) # Output: False
# Pythons Auswertung:
# Schritt 1: x < 3 → 5 < 3 → False
# Schritt 2: Da die linke Seite False ist, x > 10 nicht auswerten
# Schritt 3: False zurückgebenHier ist ein praktisches Beispiel, das zeigt, warum Short-Circuit-Auswertung wichtig ist:
# Prüfen, ob eine Zahl teilbar ist – Division durch Null vermeiden
numerator = 100
denominator = 0
# Das ist sicher wegen Short-Circuit-Auswertung
# Wenn denominator 0 ist, passiert die Division nie
is_divisible = denominator != 0 and numerator % denominator == 0
print(f"Is divisible: {is_divisible}") # Output: Is divisible: False
# Ohne Short-Circuit-Auswertung würde das einen Fehler auslösen:
# denominator = 0
# result = numerator % denominator # ZeroDivisionError!Der Ausdruck denominator != 0 wird zu False ausgewertet, daher wertet Python numerator % denominator nicht aus, was einen Fehler durch Division durch Null verursachen würde.
Sehen wir uns ein weiteres Beispiel mit String-Operationen an:
# String-Eigenschaften sicher prüfen
text = ""
# Prüfen, ob text nicht leer ist UND das erste Zeichen ein Großbuchstabe ist
# Sicher, weil wir bei leerem text nie versuchen, auf text[0] zuzugreifen
has_uppercase_start = len(text) > 0 and text[0].isupper()
print(f"Starts with uppercase: {has_uppercase_start}") # Output: Starts with uppercase: False
# Wenn wir das ohne Längenprüfung versuchen würden:
# text = ""
# result = text[0].isupper() # IndexError: string index out of range9.3.2) Wie or Short-Circuit verwendet
Beim or-Operator gilt: Wenn der linke Operand True ist, weiß Python, dass der gesamte Ausdruck True sein muss (weil es ausreicht, wenn mindestens ein Operand wahr ist). Daher wertet Python den rechten Operanden nicht aus.
# Einfache Demonstration
x = 15
result = x > 10 or x < 5
print(result) # Output: True
# Pythons Auswertung:
# Schritt 1: x > 10 → 15 > 10 → True
# Schritt 2: Da die linke Seite True ist, x < 5 nicht auswerten
# Schritt 3: True zurückgeben9.3.3) Praktische Anwendungen der Short-Circuit-Auswertung
Fehler vermeiden:
# Sicher auf Listenelemente zugreifen
numbers = [1, 2, 3]
index = 5
# Prüfen, ob index gültig ist, bevor wir darauf zugreifen
is_valid = index < len(numbers) and numbers[index] > 0
print(f"Valid and positive: {is_valid}") # Output: Valid and positive: False
# Ohne Short-Circuit würde das abstürzen:
# is_valid = numbers[index] > 0 # IndexError!Mehrere Bedingungen effizient prüfen:
# Formularvalidierung – beim ersten Fehler stoppen
email = "user@example.com"
password = "pass"
age = 25
# Jede Anforderung in der Reihenfolge prüfen, in der sie am wahrscheinlichsten fehlschlägt
valid_form = (
len(email) > 0 and # Schnelle Prüfung
"@" in email and # Schnelle Prüfung
len(password) >= 8 and # Schnelle Prüfung
age >= 18 # Schnelle Prüfung
)
print(f"Form valid: {valid_form}") # Output: Form valid: False
# Stoppt bei der Passwortlängenprüfung, wertet age nicht aus9.4) Was die Operatoren and und or mit nicht-booleschen Operanden zurückgeben und häufige Fallstricke bei booleschen Ausdrücken
Bisher haben wir gesehen, wie and, or und not mit booleschen Werten arbeiten. Aber Pythons logische Operatoren haben ein interessantes Verhalten: Sie können mit beliebigen Werten arbeiten, nicht nur mit True und False. Wenn Sie dieses Verhalten verstehen, können Sie prägnanteren Code schreiben und häufige Fehler vermeiden.
9.4.1) Truthiness und Falsiness verstehen (Wiederholung)
Wie wir in Kapitel 7 gelernt haben, behandelt Python viele nicht-boolesche Werte in booleschen Kontexten entweder als „truthy“ oder „falsy“:
Falsy-Werte (werden als False behandelt):
FalseNone0(Null jeder numerischen Art)""(leerer String)[](leere Liste){}(leeres Dictionary)()(leeres Tuple)
Truthy-Werte (werden als True behandelt):
True- Jede von Null verschiedene Zahl
- Jeder nicht leere String
- Jede nicht leere Sammlung
# Truthiness demonstrieren
if "hello":
print("Nicht leere Strings sind truthy") # Output: Non-empty strings are truthy
if 0:
print("Das wird nicht ausgegeben") # Null ist falsy
else:
print("Null ist falsy") # Output: Zero is falsy
if [1, 2, 3]:
print("Nicht leere Listen sind truthy") # Output: Non-empty lists are truthy9.4.2) Was and tatsächlich zurückgibt
Der and-Operator gibt nicht immer True oder False zurück. Stattdessen gibt er einen seiner Operanden zurück:
- Wenn der linke Operand falsy ist, gibt
andden linken Operanden zurück (ohne den rechten auszuwerten) - Wenn der linke Operand truthy ist, gibt
andden rechten Operanden zurück
# and gibt den ersten falsy Wert zurück oder den letzten Wert, wenn alle truthy sind
result = 5 and 10
print(result) # Output: 10
result = 0 and 10
print(result) # Output: 0
result = "hello" and "world"
print(result) # Output: world
result = "" and "world"
print(result) # Output: (empty string)
result = None and "world"
print(result) # Output: NoneVerfolgen wir diese Beispiele:
# Beispiel 1: Beide truthy
result = 5 and 10
# Schritt 1: 5 ist truthy, also rechte Seite auswerten
# Schritt 2: Wert der rechten Seite zurückgeben: 10
print(result) # Output: 10
# Beispiel 2: Links ist falsy
result = 0 and 10
# Schritt 1: 0 ist falsy, also sofort zurückgeben
# Schritt 2: Rechte Seite nicht auswerten
print(result) # Output: 0
# Beispiel 3: Beide Strings sind truthy
result = "hello" and "world"
# Schritt 1: "hello" ist truthy, also rechte Seite auswerten
# Schritt 2: Wert der rechten Seite zurückgeben: "world"
print(result) # Output: world9.4.3) Was or tatsächlich zurückgibt
Ähnlich gibt der or-Operator einen seiner Operanden zurück:
- Wenn der linke Operand truthy ist, gibt
orden linken Operanden zurück (ohne den rechten auszuwerten) - Wenn der linke Operand falsy ist, gibt
orden rechten Operanden zurück
# or gibt den ersten truthy Wert zurück oder den letzten Wert, wenn alle falsy sind
result = 5 or 10
print(result) # Output: 5
result = 0 or 10
print(result) # Output: 10
result = "" or "default"
print(result) # Output: default
result = "hello" or "world"
print(result) # Output: hello
result = None or 0
print(result) # Output: 0Verfolgen wir diese Beispiele:
# Beispiel 1: Links ist truthy
result = 5 or 10
# Schritt 1: 5 ist truthy, also sofort zurückgeben
# Schritt 2: Rechte Seite nicht auswerten
print(result) # Output: 5
# Beispiel 2: Links ist falsy
result = 0 or 10
# Schritt 1: 0 ist falsy, also rechte Seite auswerten
# Schritt 2: Wert der rechten Seite zurückgeben: 10
print(result) # Output: 10
# Beispiel 3: Beide falsy
result = None or 0
# Schritt 1: None ist falsy, also rechte Seite auswerten
# Schritt 2: Wert der rechten Seite zurückgeben: 0 (auch wenn es ebenfalls falsy ist)
print(result) # Output: 09.4.4) Praktische Verwendung von or für Standardwerte
Ein häufiges Muster ist, or zu verwenden, um Standardwerte bereitzustellen:
# Benutzereinstellungen mit Standardwerten
user_theme = "" # Benutzer hat kein Theme gesetzt
theme = user_theme or "light"
print(f"Theme: {theme}") # Output: Theme: light
user_theme = "dark"
theme = user_theme or "light"
print(f"Theme: {theme}") # Output: Theme: dark
# Konfigurationswerte
max_retries = None # Nicht konfiguriert
retries = max_retries or 3
print(f"Retries: {retries}") # Output: Retries: 3
max_retries = 5
retries = max_retries or 3
print(f"Retries: {retries}") # Output: Retries: 5Dieses Muster funktioniert, weil or die rechte Seite (den Standardwert) zurückgibt, wenn die linke Seite falsy ist (leerer String, None, 0 usw.).
9.4.12) Zusammenfassung dessen, was Operatoren zurückgeben
Hier ist eine umfassende Zusammenfassung dessen, was jeder logische Operator zurückgibt:
and-Operator:
- Gibt den ersten falsy Operanden zurück
- Wenn alle Operanden truthy sind, gibt er den letzten Operanden zurück
- Verwendet Short-Circuit-Auswertung (stoppt beim ersten falsy Wert)
or-Operator:
- Gibt den ersten truthy Operanden zurück
- Wenn alle Operanden falsy sind, gibt er den letzten Operanden zurück
- Verwendet Short-Circuit-Auswertung (stoppt beim ersten truthy Wert)
not-Operator:
- Gibt immer einen booleschen Wert zurück (
TrueoderFalse) notkonvertiert den Operanden zu einem booleschen Wert und negiert ihn dann
# Alle drei Operatoren demonstrieren
print(5 and 10) # Output: 10 (both truthy, return last)
print(0 and 10) # Output: 0 (first falsy, return it)
print(5 or 10) # Output: 5 (first truthy, return it)
print(0 or 10) # Output: 10 (first falsy, evaluate second)
print(not 5) # Output: False (5 is truthy, not returns boolean)
print(not 0) # Output: True (0 is falsy, not returns boolean)
print(not "") # Output: True (empty string is falsy)
print(not "hello") # Output: False (non-empty string is truthy)Wenn Sie diese Verhaltensweisen verstehen, können Sie prägnanteren und Pythonic Code schreiben, aber priorisieren Sie immer Klarheit. Wenn die Nutzung dieser Features Ihren Code schwerer verständlich macht, ist es besser, explizit zu sein.
In diesem Kapitel haben wir untersucht, wie Sie einfache Bedingungen mithilfe von Pythons and-, or- und not-Operatoren zu komplexer boolescher Logik kombinieren können. Wir haben etwas über Operatorpriorität, Short-Circuit-Auswertung und das überraschende Verhalten logischer Operatoren mit nicht-booleschen Werten gelernt. Außerdem haben wir häufige Fallstricke und Best Practices für das Schreiben klarer, korrekter boolescher Ausdrücke betrachtet.
Diese Werkzeuge ermöglichen es Ihnen, anspruchsvolle Entscheidungslogik in Ihren Programmen auszudrücken. In Kombination mit den if-Anweisungen aus Kapitel 8 können Sie nun praktisch jede bedingte Logik umsetzen, die Ihre Programme benötigen. Im nächsten Kapitel werden wir bedingte Ausdrücke untersuchen, die eine kompakte Möglichkeit bieten, basierend auf einer Bedingung zwischen zwei Werten zu wählen.