Instrukcje warunkowe w Pythonie: Pełny przewodnik po sterowaniu przepływem programu
Instrukcje warunkowe to mechanizm, który pozwala programowi podejmować decyzje na podstawie dynamicznych danych. Sterowanie przepływem (ang. control flow) decyduje o tym, które ścieżki kodu zostaną wykonane, które pominięte, a które zmodyfikowane.
W tym artykule omówimy pełny zakres wiedzy dotyczący instrukcji if, elif oraz else w Pythonie — od podstawowej składni, przez podmaski ewaluacji logicznej i strukturę pamięciową, po optymalizację wykonania oraz wzorce projektowe czystego kodu.
Składnia i mechanika bloku warunkowego
W Pythonie struktura bloku warunkowego opiera się na słowach kluczowych if, elif (skrót od else if) oraz else. Python nie używa nawiasów klamrowych {} do wyznaczania bloku kodu, lecz polega ściśle na wcięciach (indentacji), co wymusza określoną strukturę czytelności.
x = 15
if x > 20:
print("x jest większe niż 20")
elif x > 10:
print("x jest większe niż 10, ale mniejsze lub równe 20")
elif x > 5:
print("x jest większe niż 5, ale mniejsze lub równe 10")
else:
print("x jest równe 5 lub mniejsze")
Zasady ewaluacji sekwencyjnej
- Interpreter ocenia warunki w kolejności od góry do dołu.
- Gdy pierwszy warunek zwróci wartość True, odpowiadający mu blok kodu zostaje wykonany.
- Pozostałe instrukcje elif oraz else w danej gałęzi są całkowicie pomijane, nawet jeśli zawarte w nich warunki również byłyby prawdziwe.
- Blok else jest opcjonalny. Wykonuje się tylko wtedy, gdy żaden ze wcześniejszych warunków nie został spełniony (False).
Operatory porównania i tożsamości
Warunki tworzymy przy użyciu dwóch głównych kategorii operatorów: porównania wartości oraz tożsamości obiektów.
Operatory porównania wartości
| Operator: | Opis: | Przykład: |
|---|---|---|
| == | Równe | a == b |
| != | Różne | a != b |
| < | Mniejsze niż | a < b |
| > | Większe niż | a > b |
| < | Mniejsze lub równe | a <= b |
| > | Większe lub równe | a >= b |
Różnica między == a is (wartość vs tożsamość)
Jest to jedna z najważniejszych różnic w Pythonie:
| == (Równość wartości): | Sprawdza, czy obiekty mają tę samą zawartość (wywołuje metodę __eq__()). |
| is (Tożsamość obiektu): | Sprawdza, czy zmienne wskazują na ten sam adres w pamięci RAM (porównuje id obiektów za pomocą id()). |
a = [1, 2, 3] b = [1, 2, 3] print(a == b) # True -> Wynik jest taki sam (zawartość jest identyczna) print(a is b) # False -> To dwa różne obiekty w pamięci c = a print(a is c) # True -> c wskazuje na ten sam obiekt w pamięci co a
Uwaga: Do sprawdzania wartości None zawsze stosujemy operator is lub is not (if x is None:), ponieważ None jest singletonem w Pythonie.
Łączenie operatorów porównania (Chained Comparisons)
Python umożliwia naturalne, matematyczne łączenie operatorów bez konieczności stosowania spójników logicznych:
age = 25
# Zamiast: if age >= 18 and age <= 65:
if 18 <= age <= 65:
print("Wiek w przedziale produkcyjnym")
Operatory logiczne i kolejność ich wykonywania
Do budowania złożonych warunków służą operatory and, or oraz not.
| and | Zwraca True tylko wtedy, gdy oba argumenty są prawdziwe. |
| or | Zwraca True, jeśli przynajmniej jeden z argumentów jest prawdziwy. |
| not | Odwraca wartość logiczną (not True daje False). |
Precedens (kolejność) operatorów
Gdy w jednym wyrażeniu występują różne operatory, Python wykonuje je w następującej kolejności (od najwyższego priorytetu):
- Operatory porównania (==, !=, <, >, is, in)
- not
- and
- or
Aby zapobiec błędom logicznym w skomplikowanych warunkach, należy jawnie grupować wyrażenia za pomocą nawiasów ():
is_admin = False
has_permission = True
is_owner = True
# Złożony warunek z użyciem nawiasów
if (is_admin or has_permission) and is_owner:
print("Dostęp przyznany")
Mechanizm Truthy i Falsy
W Pythonie każdy obiekt ma wbudowaną wartość logiczną. Oznacza to, że nie musisz porównywać zmiennych jawnie do True lub False.
Wartości traktowane jako False (Falsy):
| Stałe | None, False |
| Zero dowolnego typu liczbowego | 0, 0.0, 0j |
| Puste sekwencje i kolekcje | "" (pusty napis), [] (pusta lista), () (pusta krotka), {} (pusty słownik), set() (pusty zbiór), range(0) |
Wszystkie inne wartości są traktowane jako True (Truthy).
Idiomatyczne wykorzystanie w kodzie (Pythonic Code):
users_list = []
# Niejęzykowo (niezalecane):
if len(users_list) == 0:
print("Brak użytkowników")
# Idiomatycznie (zalecane):
if not users_list:
print("Brak użytkowników")
Ewaluacja leniwa (Short-Circuit Evaluation)
Python optymalizuje wykonywanie warunków logicznych przy użyciu mechanizmu short-circuiting. Interpreter przerywa sprawdzanie dalszych części warunku, gdy tylko wynik całego wyrażenia stanie się przesądzony.
| Dla operatora and | Jeśli pierwszy warunek zwraca False, drugi nie zostanie wywołany, ponieważ całe wyrażenie i tak będzie fałszywe. |
| Dla operatora or | Jeśli pierwszy warunek zwraca True, drugi nie zostanie wywołany, ponieważ całe wyrażenie i tak będzie prawdziwe. |
Praktyczne zastosowanie: ochrona przed błędami
Ewaluacja leniwa pozwala zabezpieczyć kod przed wyjątkami (np. dzieleniem przez zero lub odwołaniem do nieistniejącego indeksu):
data = []
# Drugi warunek (data[0] == 5) wywołałby IndexError,
# ale nie zostanie oceniony, bo 'data' jest pusta (Falsy).
if data and data[0] == 5:
print("Pierwszy element to 5")
else:
print("Lista jest pusta lub pierwszy element to nie 5")
Ternary Operator (wyrażenie warunkowe)
Dla prostych przypisań opartych na warunku stosuje się tzw. Ternary Operator (jednoliniową instrukcję warunkową).
Składnia:
zmienna = wartość_jeśli_true if warunek else wartość_jeśli_false
Przykład:
age = 20
# Standardowy zapis:
if age >= 18:
status = "Pełnoletni"
else:
status = "Niepełnoletni"
# Zapis jednoliniowy (Ternary Operator):
status = "Pełnoletni" if age >= 18 else "Niepełnoletni"
Dobra praktyka: Stosuj ternary operator tylko dla prostych warunków. Unikaj zagnieżdżania wyrażeń ternary (a if cond1 else b if cond2 else c), ponieważ drastycznie obniża to czytelność kodu.
Wzorzec Guard Clauses (klauzule strażnicze)
Głębokiego zagnieżdżania instrukcji if (tzw. Arrow Anti-pattern) należy unikać. Utrudnia ono analizę przepływu kodu i prowadzi do błędów.
Kod nieoptymalny (drzewko decyzyjne):
def process_order(order):
if order is not None:
if order.is_paid:
if order.has_stock:
order.ship()
return "Wysłano"
else:
return "Brak towaru"
else:
return "Nieopłacono"
else:
return "Brak zamówienia"
Kod optymalny z klauzulami strażniczymi (Guard Clauses):
Klauzula strażnicza to wczesne przerwanie funkcji (return lub raise) w przypadku niespełnienia warunków wstępnych.
def process_order(order):
if order is None:
return "Brak zamówienia"
if not order.is_paid:
return "Nieopłacono"
if not order.has_stock:
return "Brak towaru"
# Główna logika znajduje się na głównym poziomie wcięcia
order.ship()
return "Wysłano"
Structural Pattern Matching (instrukcja match/case)
Wdrożona w Pythonie 3.10 instrukcja match/case jest potężnym odpowiednikiem switch/case z innych języków, rozszerzonym o mechanizm dopasowywania wzorców (Pattern Matching).
def HTTP_status_handler(status_code):
match status_code:
case 200:
return "OK"
case 400 | 404: # Dopasowanie alternatywne (OR)
return "Błąd klienta"
case 500 | 502 | 503:
return "Błąd serwera"
case _: # Odpowiednik 'else' / wartość domyślna
return "Nieznany kod stanu"
Podsumowanie i checklist dobrych praktyk
- Stosuj PEP 8: Używaj wcięć złożonych z 4 spacji na każdy poziom zagnieżdżenia.
- Korzystaj z Truthy/Falsy: Pisz if not collection: zamiast if len(collection) == 0:.
- Pamiętaj o is przy None: Pisz if val is None: zamiast if val == None:.
- Wykorzystuj short-circuiting: Układaj warunki od najbardziej prawdopodobnych lub najmniej kosztownych obliczeniowo.
- Redukuj zagnieżdżenia: Używaj Guard Clauses, aby utrzymać kod płaski i czytelny.
Opis przykładu praktycznego
Jako zwieńczenie omówionych koncepcji stworzyliśmy lekki skrypt do automatycznego monitorowania dostępności serwisów internetowych (Web Health Checker). Program przyjmuje od użytkownika adres URL, po czym wykonuje zapytanie HTTP/HTTPS i generuje raport w czasie rzeczywistym.
Przykładowy kod demonstruje wszystkie omówione wzorce w działaniu:
| Guard Clauses (klauzule strażnicze) | linia sprawdzająca poprawność wprowadzonych danych wejściowych natychmiast przerywa działanie funkcji przy pustym ciągu znaków, zapobiegając niepotrzebnemu wykonywaniu dalszego kodu. |
| Modyfikacja dynamiczna i pętle | skrypt automatycznie dopisuje protokół https://, jeśli użytkownik go pominie, oraz pobiera nowy adres URL bezpośrednio przez konsolowy prompt input(). |
| Struktura if / elif / else | kod odpowiedzi HTTP jest klasyfikowany na statusy powodzenia (200), przekierowania (300+) lub błędu. |
| Ternary Operator (Operator trójargumentowy) | jednolinijkowa ocena czasu odpowiedzi serwera przydziela etykietę "Szybko" (poniżej 300 ms) lub "Wolno". |
| Short-Circuit Evaluation & Truthy Check | w głównej pętli warunek if report and report["code"] bezpiecznie weryfikuje strukturę danych przed wyświetleniem wyniku na ekranie. |
Pobierz Web Health Checker (tester czasu reakcji stron)
| Źródło: | Nazwa: |
|---|---|
| Centrum Pobierania | post_51pl |
👇
▒ Przewodnik po blogu: wszystkie posty o programowaniu w jednym miejscu. ▹ Zobacz