06 - Instrukcje wyboru
Jeżeli… - czyli instrukcje wyboru (if
, elif
, else
)
Kod, który pisaliśmy dotychczas wykonywał się od początku do końca zawsze w ten sam sposób. Bardziej złożone programy, zwykle potrafią w różny sposób sterować przebiegiem działania w zależności, na przykład od tego, jakie dane wprowadzimy. Instrukcją, która to umożliwia jest if
, której często towarzyszą elif
i else
. Zacznijmy od prostego przykładu. Uwaga! Przepisując kod pilnuj odpowiedniego przesunięcia (czyli wcięcia) kodu, tak jak to napisano poniżej.
# Przypisanie wartości boolowskiej do zmiennej test
test = True
# Jeżeli zmienna test ma wartość true to napisz "prawda"
if test:
print("Prawda")
Prawda
Jak widać, program wydrukował Prawda
.
Teraz nieco zmodyfikujmy kod, przypisując zmiennej test
wartość False
:
# Przypisanie wartości boolowskiej do zmiennej test
test = False
# Jeżeli zmienna test ma wartość true to napisz "prawda"
if test:
print("Prawda")
Tym razem nie ma żadnego wyniku.
Po instrukcji if
znajduje się wyrażenie, które powinno zwracać wartość True
lub False
. Następnie występuje znak :
. Kolejna linia jest ,,wcięta’’ w stosunku do poprzedniej. Zaleca się, żeby takie wcięcie miało cztery spacje. Edytory, które są dostosowane do programowania w tym języku, powinny po naciśnięciu Enter-a po dwukropku automatycznie wprowadzić w kolejnej linii (i następnych) odpowiedni odstęp. Linie kodu które są przesunięte, stanowią blok kodu, który będzie wykonywany jeżeli wyrażenie po if
zwróci wartość True
.
Jeśli dalej wpiszemy kolejne linie kodu, ale już bez wcięcia, będą wykonywane tak, czy inaczej:
# Przypisanie wartości boolowskiej do zmiennej test
test = False
# Jeżeli zmienna test ma wartość true to napisz "prawda"
if test:
print("Prawda")
print(f"Wartość zmiennej `test` = {test}.\nKoniec programu.")
Wartość zmiennej `test` = False.
Koniec programu.
Warto w tym miejscu podkreślić, że w przeciwieństwie do wielu innych języków, np. Javy, blok kodu wykonywany po wyrażeniach typu if
, nie jest specjalnie ograniczony szczególnymi znakami ({
i }
) ale jego granice ograniczają właśnie wcięcia.
Taki blok może mieć wiele linii, każda powinna być tak samo przesunięta w odniesieniu do początku linii, o ile nie występują w nim inne fragmenty kody wymagające dalszego wcięcia. W wielu językach takie wcięcia mają znaczenie wyłącznie dla czytelności kodu, w Pythonie są obowiązkowe.
Wcięcia mogą być tworzone za pomocą tabulatorów lub spacji, przy czym należy konsekwentnie używać jednego lub drugiego typu znaków w kodzie, nie obu. Zaleca się jednak, żeby do wcięć używać spacji w liczbie 4, lub przy zagnieżdżonych wcięciach (o czym za chwilę), wielokrotność 4 (8, 12 itd.).
Powyższy kod sprawdzał wartość zmiennej test
i wykonywał kod, jeżeli zwracała ona wartość True
drukowany był tekst ,,Prawda’’, jeśli zwracana była wartość False
nic się nie działo. Często jednak chcielibyśmy, żeby w obu przypadkach był wykonywany jakiś kod. Można to zrobić w ten sposób:
# Przypisanie wartości boolowskiej do zmiennej test
test = False
# Jeżeli zmienna test ma wartość true to napisz "prawda"
if test:
print("Prawda")
# W innym przypadku napisz "Fałsz"
else:
print("Fałsz")
print(f"Wartość zmiennej `test` = {test}.\nKoniec programu.")
Fałsz
Wartość zmiennej `test` = False.
Koniec programu.
Teraz, jeśli zmienna test
nie zwraca wartości True
wypisuje ,,Fałsz’’. Ten program ma jednak tę wadę, że tak naprawdę nie sprawdza, czy zmienna zwraca wartość False
, w takim prostym przypadku to wystarcza, ale nie zawsze.
Napiszmy teraz program, który sprawdzi, czy do zmiennej jest przypisane True
, False
, czy może jakaś inna wartość. Będziemy mieli w tym przypadku trzy możliwości:
- Zmiennej jest przypisana wartość
True
. - Zmiennej jest przypisana wartość
False
. - Zmiennej jest przypisana jakaś inna wartość.
Przyda się tu kolejne wyrażenie: elif
, które można rozumieć jako skrót od ,,else if’’. Występuje ono po if
i podobnie jak po if
umieszczamy po nim wyrażenie, które powinno zwracać True
, lub False
. Wykonuje się ono wtedy, gdy if
zwraca False
. Modyfikację naszego programu zacznijmy od przypisania innej wartości zmiennej test
:
# Przypisanie wartości do zmiennej test
test = 4
# Jeżeli zmienna test ma wartość true to napisz "prawda"
if test:
print("Prawda")
# W innym przypadku napisz "Fałsz"
else:
print("Fałsz")
print(f"Wartość zmiennej `test` = {test}.\nKoniec programu.")
Prawda
Wartość zmiennej `test` = 4.
Koniec programu.
Wynik może być początkowo zaskakujący. Przypomnij sobie jednak lekcję o zmiennych. Liczby inne niż 0 w przypadku konwersji na wartość boolean
(a taka tu zachodzi) konwertowane są na True
.
Zmodyfikujmy nieco nasz program:
# Przypisanie wartości do zmiennej test
test = 4
# Jeżeli zmienna test ma wartość true to napisz "prawda"
if test == True:
print("Prawda")
# W innym przypadku napisz "Fałsz"
else:
print("Fałsz")
print(f"Wartość zmiennej `test` = {test}.\nKoniec programu.")
Fałsz
Wartość zmiennej `test` = 4.
Koniec programu.
Tym razem zastosowaliśmy operator ==
, który służy do testowania równości, zwróć uwagę, że składa się on z dwu znaków =
. Nie należy go mylić, co jest częstym błędem początkującego programisty z operatorem przypisania =
. Jeśli w programie, który będziesz pisać występują tylko możliwe wartości True
lub False
zamiast używania operatora ==
zalecane jest raczej stosowanie metody testowania stosowanej poprzednio.
Teraz uzupełnimy nasz program używając wyrażenia elif
:
# Przypisanie wartości do zmiennej test
test = 4
# Jeżeli zmienna test ma wartość true to napisz "prawda"
if test == True:
print("Prawda")
# W innym przypadku sprawdź czy "False"
elif not test:
print("Fałsz")
# Pozostałe przypadki
else:
print("Inna wartość")
print(f"Wartość zmiennej `test` = {test}.\nKoniec programu.")
Inna wartość
Wartość zmiennej `test` = 4.
Koniec programu.
Zauważ, że do sprawdzenia czy zmienna ma wartość false
użyliśmy operatora not
. Jest to operator logiczny, który odwraca znaczenie wyrażenia znajdującego się po nim, czyli np. odwraca True
na False
.
Sprawdź jak zachowa się powyższy skrypt, jeśli zmiennej przypiszemy wartości 0
, 1
, "A"
. Wyjaśnij wyniki.
Operatory porównań
Operator ==
jest jednym z operatorów relacji (porównań). Pozwala na porównanie dwu wartości i uzyskanie wartości True
lub False
w zależności od tego czy są one równe, czy nie. Na przykład:
# Porównanie dwu liczb.
# Pobranie liczb od użytkownika
liczba1 = int(input('Podaj pierwszą liczbę całkowitą: '))
liczba2 = int(input('Podaj drugą liczbę całkowitą: '))
# Sprawdzenie, czy liczby są równe
if liczba1 == liczba2:
print('Liczby są równe')
else:
print('Liczby różnią się')
Podaj pierwszą liczbę całkowitą: 2
Podaj drugą liczbę całkowitą: 3
Liczby różnią się
Jeśli chcielibyśmy sprawdzić, czy liczby nie są równe, możemy użyć operatora !=
:
# Porównanie dwu liczb.
# Pobranie liczb od użytkownika
liczba1 = int(input('Podaj pierwszą liczbę całkowitą: '))
liczba2 = int(input('Podaj drugą liczbę całkowitą: '))
# Sprawdzenie, czy liczby są równe
if liczba1 != liczba2:
print("Liczby różnią się")
else:
print("Liczby są równe")
Podaj pierwszą liczbę całkowitą: 2
Podaj drugą liczbę całkowitą: 3
Liczby różnią się
Kolejne dwa operatory, znane nam z lekcji matematyki to >
(większy) i <
(mniejszy). Zmodyfikujmy nasz program:
# Porównanie dwu liczb.
# Pobranie liczb od użytkownika
liczba1 = int(input('Podaj pierwszą liczbę całkowitą: '))
liczba2 = int(input('Podaj drugą liczbę całkowitą: '))
# Sprawdzenie, czy liczba1 jest większa niż liczba2
if liczba1 > liczba2:
print("Pierwsza liczba jest większa niż druga liczba")
# Sprawdzenie, czy liczba1 jest mniejsza niż liczba2
elif liczba1 < liczba2:
print("Pierwsza liczba jest mniejsza niż druga liczba")
# Jeśli liczba1 nie jest ani większa ani mniejsza niż liczba2
# to znaczy, że są równe
else:
print("Liczby są równe")
Podaj pierwszą liczbę całkowitą: 2
Podaj drugą liczbę całkowitą: 7
Pierwsza liczba jest mniejsza niż druga liczba
Do kompletu brakuje nam jeszcze operatorów: >=
(większy lub równy) oraz <=
(mniejszy lub równy). Ważne, aby nie pomylić kolejności znaków w tych wyrażeniach.
Podsumujmy poznane operatory relacji (porównań):
Operator | znaczenie | przykład | wynik |
---|---|---|---|
== | równość | 2 == 4 | False |
!= | nierówność | 2 != 4 | True |
> | większe | 2 > 4 | False |
< | mniejsze | 2 < 4 | True |
>= | większe lub równe | 2 >= 4 | False |
<= | mniejsze lub równe | 2 <= 4 | True |
Porównywanie ciągów znaków
Powyżej porównywaliśmy liczby, ale można także porównać ciągi znaków:
# Porównanie ciągów znaków
# Pobranie odpowiedzi od użytkownika
odpowiedz = input('Podaj hasło: ')
# Porównanie podanego hasła z właściwym
if odpowiedz == 'Python':
print('Witaj w skarbcu!')
else:
print('Niestety, błedne hasło!')
Podaj hasło: Python
Witaj w skarbcu!
Teraz bardziej złożony przykład:
# Znaczenie skrótów zasad azotowych
# Pobranie litery od użytkownika:
litera = input('Podaj literę oznaczającą zasadę azotową: ')
if litera == 'A':
print(f'Litera {litera} to adenina')
elif litera == 'C':
print(f'Litera {litera} to cytozyna')
elif litera == 'G':
print(f'Litera {litera} to guanina')
elif litera == 'T':
print(f'Litera {litera} to tymina')
elif litera == 'U':
print(f'Litera {litera} to uracyl')
else:
print(f'Podałeś {litera} - błędna litera')
Podaj literę oznaczającą zasadę azotową: G
Litera G to guanina
Pisząc tego typu programy, pamiętaj aby przetestować możliwie szerokie spektrum możliwych danych. Tu na przykład należałoby sprawdzić, czy program podaje poprawne odpowiedzi dla każdej z liter odpowiadających zasadom a także ciągów znaków spoza tej piątki.
Powyższy program ma tę wadę, że jeśli użytkownik poda małą, zamiast wielkiej litery to uzyska komunikat o błędnej literze, np:
Podaj literę oznaczającą zasadę azotową: a
Podałeś a - błędna litera
Ćwiczenie: Zastanów się jak uniknąć tej niedogodności.
Można to zrobić w ten sposób:
# Znaczenie skrótów zasad azotowych
# Pobranie litery od użytkownika:
litera = input('Podaj literę oznaczającą zasadę azotową: ')
# Sprawdzenie znaczenia poszczególnych liter
if litera.upper() == 'A':
print(f'Litera {litera} to adenina')
elif litera.upper() == 'C':
print(f'Litera {litera} to cytozyna')
elif litera.upper() == 'G':
print(f'Litera {litera} to guanina')
elif litera.upper() == 'T':
print(f'Litera {litera} to tymina')
elif litera.upper() == 'U':
print(f'Litera {litera} to uracyl')
else:
print(f'Podałeś {litera} - błędna litera')
Podaj literę oznaczającą zasadę azotową: a
Litera a to adenina
Mozna skrócić kod:
# Znaczenie skrótów zasad azotowych
# Pobranie litery od użytkownika:
litera = input('Podaj literę oznaczającą zasadę azotową: ').upper()
if litera == 'A':
print(f'Litera {litera} to adenina')
elif litera == 'C':
print(f'Litera {litera} to cytozyna')
elif litera == 'G':
print(f'Litera {litera} to guanina')
elif litera == 'T':
print(f'Litera {litera} to tymina')
elif litera == 'U':
print(f'Litera {litera} to uracyl')
else:
print(f'Podałeś {litera} - błędna litera')
Podaj literę oznaczającą zasadę azotową: g
Litera G to guanina
Kolejnym słowem kluczowym, które możemy wykorzystać jest in
. Spójrz na poniższy przykład i zastanów się jak działa:
# Pobranie liczby od użytkownika
slowo = input('Podaj słowo: ')
litera = input('Podaj literę: ')
if litera in slowo:
print(f"Litera '{litera}' znajduje się w słowie '{slowo}'")
else:
print(f"Litera '{litera}' nie znajduje się w słowie '{slowo}'")
Podaj słowo: nukleotyd
Podaj literę: t
Litera 't' znajduje się w słowie 'nukleotyd'
- Zadanie: Spróbuj zmodyfikować program tak, żeby nie był ,,czuły’’ na wielkość liter.
Operatory logiczne
Kolejny przykład będzie także nawiązywał do rozpoznawania zasad azotowych. Tym razem jednak będziemy chcieli, żeby po podaniu przez użytkownika litery, uzyskać odpowiedź, czy odpowiada ona zasadzie purynowej (A lub G) czy pirymidynowej (C, T lub U).
Spróbuj samodzielnie napisać taki program.
Zapewne będzie on wyglądał mniej więcej tak:
# Program rozpoznający zasady purynowe i pirymidynowe
# Pobranie litery od użytkownika
litera = input('Podaj symbol zasady: ').upper()
# Sprawdzenie czy litera odpowiada zasadzie purynowej, czy pirymidynowej.
if litera == 'A':
print(f'{litera} to zasada purynowa')
elif litera == 'G':
print(f'{litera} to zasada purynowa')
elif litera == 'C':
print(f'{litera} to zasada pirymidynowa')
elif litera == 'T':
print(f'{litera} to zasada pirymidynowa')
elif litera == 'U':
print(f'{litera} to zasada pirymidynowa')
else:
print(f'Podałeś {litera} - błędna litera')
Podaj symbol zasady: T
T to zasada pirymidynowa
Jak widać program działa. Jednak zauważ, że zawiera kilkukrotne powtórzenia tych samych fragmentów kodu. Dla każdej z liter odpowiadającej zasadzie purynowej wykonywany jest ten sam kod: print(f'{litera} to zasada purynowa')
, który jest powtórzony dwa razy, dla zasad pirymidynowych mamy trzy powtórzenia. Pojawia się pytanie czy nie można by stworzyć testu w rodzaju: ,,jeżeli litera == A LUB litera == G’’? Odpowiedź na nie jest na szczęście twierdząca.
Z pomocą przychodzą na operatory logicze, które pozwalają łączyć warunki w różny sposób. Pierwszy z nich to or
, który oznacza ,,lub’’. Możemy teraz zmodyfikować nasz program:
# Program rozpoznający zasady purynowe i pirymidynowe
# Pobranie litery od użytkownika
litera = input('Podaj symbol zasady: ').upper()
# Sprawdzenie czy litera odpowiada zasadzie purynowej, czy pirymidynowej.
if litera == 'A' or litera == 'G':
print(f'{litera} to zasada purynowa')
elif litera == 'C' or litera == 'T' or litera == 'U':
print(f'{litera} to zasada pirymidynowa')
else:
print(f'Podałeś {litera} - błędna litera')
Podaj symbol zasady: T
T to zasada pirymidynowa
Teraz kod jest krótszy, bardziej przejrzysty a także łatwiejszy w ewentualnej modyfikacji. Gdybym na przykład stwierdził, że zamiast komunikatu T to zasada pirymidynowa
powinno się wyświetlać Litera T, oznacza zasadę pirymidynową
, musiałbym zmodyfikować dwie linie kodu a nie pięć.
Staraj się, na ile to możliwe, unikać zbędnych powtórzeń kodu. Wrócimy jeszcze wielokrotnie do tego zagadnienia.
Operator or
łączy warunki, a zwracana jest wartość True
jeśli choć jeden z nich zwraca wartość True
. Można to łatwo przetestować:
wynik = False or False or True
print(wynik)
True
Następnym operatorem logicznym jest and
, czyli ,,oraz’’. W tym przypadku zwracana jest wartość True
, jeżeli wszystkie łączone wyrażenia zwracają True
:
wynik = True and True and False
print(wynik)
False
Napiszmy krótki program ilustrujący działanie operatora and
. Będzie sprawdzał, czy podana liczba jest większa niż 0 i mniejsza niż 10.
# Program sprawdzający, czy podana liczba jest większa niż 0 i mniejsza niż 10
# Pobranie liczby od użytkownika
liczba = float(input('Podaj liczbę: '))
# Sprawdzanie, czy liczba jest większa niż 0 i mniejsza niż 10
if liczba>0 and liczba<10:
print(f'Liczba {liczba} jest większa niż 0 i mniejsza niż 10')
else:
print(f'Liczba {liczba} nie jest większa niż 0 i mniejsza niż 10')
Podaj liczbę: 9.9
Liczba 9.9 jest większa niż 0 i mniejsza niż 10
Operatory logiczne można łączyć ze sobą. W celu zapewnienia odpowiedniej kolejności wykonywania testów (podobnie jak w przypadku obliczeń) stosuje się pary nawiasów (
)
.
Napiszemy teraz krótki program, który rozpoznaje czy podana przez użytkownika liczba jest parzysta LUB podzielna przez 3, ORAZ mieści się w zakresie od 100 (włącznie) do 1000 (wyłączając 1000).
Najpierw zastanów się, jak można sprawdzić czy liczba jest parzysta.
Można tu użyć operatora %
, który zwraca resztę z dzielenia. Zatem, jeśli liczba jest parzysta, wyrażenie liczba%2
, powinna zwrócić 0
.
Można to sprawdzić tak:
# Pobranie liczby od użytkownia
liczba = int(input('Podaj liczbę: '))
if liczba%2 == 0:
print(f"Liczba {liczba} jest parzysta")
else:
print(f"Liczba {liczba} nie jest parzysta")
Podaj liczbę: 8
Liczba 8 jest parzysta
Analogicznie sprawdzimy, czy liczba jest podzielna przez trzy. Pozostałe warunki są trywialne. Połączmy je w całość:
# Pobranie liczby od użytkownia
liczba = int(input('Podaj liczbę: '))
if (liczba%2==0 or liczba%3==0) and (liczba>=100 and liczba<1000):
print(f"Liczba {liczba} spełnia warunki")
else:
print(f"Liczba {liczba} nie spełnia warunków")
Podaj liczbę: 66
Liczba 66 nie spełnia warunków
Przetestuj program dla różnych liczb, zauważ, że warunki w jednej oraz drugiej parze nawiasów muszą być spełnione, żeby całe wyrażenie zwróciło wartość true
.
Sprawdź wynik dla 2000
Usuń nawiasy, sprawdź jak teraz działa program dla wartości 2000.
Zastanów się dlaczego teraz program zwrócił inny wynik.
Operatory w Pythonie mają swoją ,,ważność’’, która decyduje o kolejności ich wykonywania. Podobnie jak w operacjach matematycznych najpierw mnożymy a potem dzielimy (*
ma wyższy priorytet niż +
), trzeba także uwzględnić odpowiednie priorytety, które mają inne operatory włącznie z and
czy or
(ten pierwszy ma wyższy priorytet).
Priorytety operatorów w Pythonie są zestawione w dokumentacji: https://docs.python.org/3.8/reference/expressions.html#operator-precedence.
Odpowiednio używane nawiasy pozwalają (podobnie jak w równaniach matematycznych) ominąć domyślne priorytety operatorów.
Operator and
można połączyć ze słowem kluczowym not
, w takim przypadku, jak można się domyślić oznacza zaprzeczenie and
. Przykładowo, kolejny program:
# Pobranie liczby od użytkownia
liczba = int(input('Podaj liczbę: '))
if liczba%2==0 and not liczba%3==0:
print(f"Liczba {liczba} jest podzielna przez 2 ale nie jest podzielna przez 3")
else:
print(f"Liczba {liczba} nie spełnia warunków")
Podaj liczbę: 4
Liczba 4 jest podzielna przez 2 ale nie jest podzielna przez 3
Zagnieżdżanie
Instrukcje if
(jak i wiele innych) można zagnieżdżać. Zobaczmy to na przykładzie mini gry przygodowej. Najpierw pobierz plik z grą, uruchom ją, zagraj kilka razy wybierając różne opcje a potem przeanalizuj kod. Przy okazji zwróć uwagę jak można podzielić długą linię tekstu na kilka krótszych (inne sposoby poznaliśmy wcześniej):
## Mini gra przygodowa:
imie = input("Jesteś wojownikiem, który znalazł się w ciemnym lesie, który ludzie nazywają " +
"Smoczym Lasem. \nPodaj swoje imię: ")
sciezka = input(f"{imie}, stoisz na ścieżce, która się rozwidla, gdzie idziesz?\n" +
"p - w prawo, l - w lewo: ")
if sciezka == 'l':
wybor = input("Widzisz wejście do jaskini. Wchodzisz?\n" +
"tak - t, nie - n :")
if wybor == 't':
print(f"{imie}! Znalazłeś skarb!")
elif wybor == 'n':
print("Szkoda...")
else:
print("Nie ma takiej opcji.")
elif sciezka == 'p':
wybor = input("Widzisz wielką smoczycę. Co robisz?\n" +
"w - walczę, u - uciekam: ")
if wybor == 'w':
print(f"{imie}! To nie jest najlepszy pomysł napadać na smoczycę z gołymi rękami. " +
"Smoczyca Cię zjadła!")
elif wybor == 'u':
wybor = input("Ocaliłeś życie. Natykasz się na małego smoczka. Co robisz?\n" +
"o - oddaję smoczycy, b - biorę do domu, z - zostawiam w spokoju: ")
if wybor == 'o':
print("Smoczyca jest zachwycona, w nagrodę daje Ci worek złota.")
elif wybor == 'b':
imie_smoka = input("Zabrałeś smoka do domu. Ciekawe, czy to dobry pomysł? " +
"Jak dałeś mu na imię?\n")
print(f"Smok {imie_smoka} rósł w Twoim domu, towarzysząc Ci w codziennym życiu.\n" +
"Jednak pewnego dnia, kiedy zapomniałeś go nakarmić, zjadł Cię " +
"i odleciał do smoczego lasu. \n" +
"Morał: nie zabieraj ze sobą dzikich zwierząt.")
elif wybor == 'z':
print("Mądry wybór, choć może coś Cię ominęło?")
else:
print("Nie ma takiej opcji.")
else:
print("Nie ma takiej opcji. Niestety, smoczyca nie czekała na własciwą decyzję" +
"i Cię zjadła.")
else:
print("Nie ma takiej ścieżki.")
print("Koniec przygody.")
Jesteś wojownikiem, który znalazł się w ciemnym lesie, który ludzie nazywają Smoczym Lasem.
Podaj swoje imię: Kajko
Kajko, stoisz na ścieżce, która się rozwidla, gdzie idziesz?
p - w prawo, l - w lewo: l
Widzisz wejście do jaskini. Wchodzisz?
tak - t, nie - n : n
Szkoda...
Koniec przygody.
Jak widać, zagnieżdzając instrukcje wyboru, możemy kierować przebieg programu w różnych kierunkach, w zależności od wprowadzonych danych, wyników obliczeń itp.
Zadanie:
Napisz samodzielnie własną grę przygodową.