article banner (priority)

Wyrażenia logiczne w Pythonie

Do tej pory widzieliśmy zaledwie bardzo proste warunki sprawdzające wartość jednej zmiennej. Jak zachować się w sytuacji, gdy chcielibyśmy wykonać akcję, w której jednocześnie dwa warunki są spełnione? Na przykład:

  • czy tweet jest promowany i pochodzi od osoby obserwowanej,
  • czy tweet jest własny lub pochodzi od osoby obserwowanej.

Aby uzyskiwać odpowiedzi na takie pytania, potrzebujemy operatorów logicznych and, or i not, które to wykonują operacje na wartościach logicznych (podobnie jak +, - i * wykonują operacje na liczbach).

Operator and

Aby dobrze zrozumieć operatory logiczne, przytoczę historię małego Jasia. Chciał on pograć na komputerze, więc poszedł do mamy poprosić o pozwolenie. Ta jednak powiedziała: "Będziesz mógł pograć, gdy odrobisz lekcje i posprzątasz pokój".

Ten warunek w programowaniu mógłby być wyrażony następująco:

can_play_games = finished_homework and cleaned_room

Widzimy tam zmienne finished_homeworkcleaned_room, reprezentujące kolejno ukończenie zadań domowych i posprzątanie pokoju (zakładamy, że są to wartości logiczne, czyli True lub False). Pomiędzy nimi widzimy natomiast operator and1, który sprawia, że całe wyrażenie jest True tylko wtedy, gdy obydwie jego strony są True2.

aba and b
TrueTrueTrue
TrueFalseFalse
FalseTrueFalse
FalseFalseFalse
print(True and True)  # True
print(True and False)  # False
print(False and True)  # False
print(False and False)  # False

# A czy Jasio może mieć szczeniaka?
have_time = True
is_responsible = False
can_have_puppy = is_responsible and have_time
print(can_have_puppy)  # False

Za przykładowe częste wykorzystanie tego operatora uznaje się sprawdzenie, czy liczba znajduje się w jakimś zakresie: czy jest większa od minimum i mniejsza niż maksimum. Dla przykładu moglibyśmy sprawdzić, czy zmienna percent ma poprawną wartość, czyli czy znajduje się między 0 a 100.

if percent >= 0 and percent <= 100:
  print("Poprawna wartość")

Jeśli percent będzie miał wartość z zakresu od 0 do 100, to zostanie wyświetlona "Poprawna wartość". W przeciwnym wypadku nic się nie stanie.

Operator or

Niestety Jasio nie odrobił lekcji, zatem nie mógł pograć na komputerze. Wtedy jednak zadzwonili do niego znajomi i zaprosili go do kina. Jasio poszedł do mamy i poprosił o pieniądze na wyjście. Ta powiedziała: "Dostaniesz pieniądze, jeśli posprzątasz garaż lub umyjesz samochód".

Ten warunek można przedstawić następująco:

can_go_to_cinema = cleaned_garage or washed_car

Operator or3 reprezentuje alternatywę. Całe wyrażenie zwraca True, gdy spełnione jest albo cleaned_garage, albo washed_car, albo oba te warunki jednocześnie.

aba or b
TrueTrueTrue
TrueFalseTrue
FalseTrueTrue
FalseFalseFalse
print(True or True)  # True
print(True or False)  # True
print(False or True)  # True
print(False or False)  # False

# Czy Jasio dostanie czekoladę?
behaved_well = True
cleaned_room = False
can_eat_chocolate = behaved_well or cleaned_room
print(can_eat_chocolate)  # True

Jasio tak się zapalił do pracy, że zarówno posprzątał garaż, jak i umył samochód. Zdziwiona mama powiedziała: "Chciałam, byś zrobił jedno albo drugie, a nie obydwa", a Jasio odpowiedział: "Użyłaś łącznika lub, który akceptuje zrobienie zarówno jednego, jak i drugiego, podobnie jak or w programowaniu". Mama Jasia zaakceptowała to wyjaśnienie, więc poszedł on z kolegami do kina.

Operator not

Ostatnim operatorem logicznym, który powinniśmy poznać, jest zaprzeczenie, czyli not przed wartością logiczną. Słowo "not" oznacza "nie". Podobnie jak - (minus) przed liczbą zamienia jej znak na odwrotny, tak not przed wartością logiczną zamienia ją na odwrotną. Operator negacji not zamienia True na False, a False na True.

cond!cond
TrueFalse
FalseTrue
print(not True)  # False
print(not False)  # True

is_amazing = True
print(not is_amazing)  # False

is_boring = False
print(not is_boring)  # True

# Podobnie jak przy liczbach
positive = 1
print(-positive)  # -1

negative = -1
print(-negative)  # 1

Programowanie akceptuje podwójne zaprzeczenie, co oznacza, że każdy kolejny znak zaprzeczenia odwraca wartość logiczną4.

print(True)  # True
print(not True)  # False
print(not not True)  # True
print(not not not True)  # False
print(not not not not True)  # True

Operator negacji not stawia się często w bardziej złożonym wyrażeniu przed pojedynczą zmienną. Jasio zrozumiał to wtedy, gdy jego mama powiedziała: "Będziesz mógł grać na komputerze, jeśli nie zawalisz sprawdzianu z matematyki i posprzątasz pokój". Wykorzystał on zaprzeczenie, by przełożyć to zdanie na język Python.

can_play_games = not failed_math_test and cleaned_room

Zwróć uwagę, że negacja odnosi się tutaj tylko do niezawalenia sprawdzianu. not True and False zwraca False, ponieważ najpierw not True zwraca False, a następnie False and False zwraca False. Podobnie jak przy liczbach -10 + 11 równe jest 1, a nie -21. Jeśli chcielibyśmy, aby zaprzeczenie negowało większy obszar wyrażenia, powinniśmy cały ten fragment otoczyć nawiasami. not (True and False) zwraca True, bo najpierw True and False zwraca False, które jest następnie negowane przez not.

# Sprawdzian zaliczony
failed_math_test = False

# Pokój nieposprzątany
cleaned_room = False

# Nie zawaliłeś sprawdzianu i posprzątałeś pokój
print(not failed_math_test and cleaned_room)  # False

# Nie jest tak, że 
# zawaliłeś sprawdzian i posprzątałeś pokój
print(not (failed_math_test and cleaned_room))  # True

Czytanie wyrażeń logicznych

Warto poćwiczyć czytanie wyrażeń logicznych na głos. Ja zwykle robię to następująco:

  • and czytam jako i.
  • or czytam jako lub.
  • not przed zmienną czytam jako nie, a przed nawiasem jako nie jest tak, że.

Poćwiczmy to na poniższych przykładach, dla zmiennych:

  • is_grounded jako "ma szlaban"
  • cleaned_room jako "posprzątał pokój"
  • passed_test jako "zaliczył sprawdzian"

Przeczytaj poniższe zdania:

  • cleaned_room and passed_test - posprzątał pokój i zaliczył sprawdzian.
  • not is_grounded or passed_test - nie ma szlabanu lub zaliczył sprawdzian.
  • cleaned_room and not passed_test - posprzątał pokój i nie zaliczył sprawdzianu.
  • not (is_grounded or not passed_test) - nie jest tak, że ma szlaban lub nie zaliczył sprawdzianu.
  • not (not cleaned_room and not passed_test) - nie jest tak, że nie posprzątał pokoju i nie zaliczył sprawdzianu. Jest to równoznaczne z cleaned_room or passed_test.

not (not a and not b) jest zawsze równoznaczne z a or b, a not (not a or not b) jest równoznaczne z a and b. Takie obrócenia stosuje się, aby pozbyć się ciężkiego do przeczytania zaprzeczenia. Przykładowo, jeśli mielibyśmy warunek not (has_computer and not is_grounded) to moglibyśmy wykorzystać fakt, że not (not a and not b) to a or b, i skrócić to wyrażenie do not has_computer or is_grounded (not przed is_grounded zniknął, bo not not a to po prostu a).

Ćwiczenie: Złożone wyrażenia logiczne

Co zostanie wypisane w wyniku działania poniższego kodu?

has_computer = True
passed_test = False
is_grounded = False

print(has_computer and passed_test)
print(passed_test or is_grounded)
print(has_computer and not passed_test)

can_play_games = has_computer and not is_grounded
print(can_play_games)

play_games = has_computer and can_play_games

if not passed_test:
  is_grounded = True

print(play_games)
print(passed_test or not is_grounded)
print(not (not has_computer or not passed_test))

Odpowiedzi na końcu książki.

1:

"and" po angielsku oznacza "i".

2:

Precyzyjniej mówiąc, całe wyrażenie jest truthy, gdy obydwie strony są truthy, nie będę jednak rozwijał bardziej tego tematu.

3:

"or" po angielsku oznacza "lub".

4:

Bawiąc się językiem, możemy powiedzieć: nie jest tak, że programowanie nie akceptuje podwójnego zaprzeczenia. A nawet (nie nie nie nie) akceptuje wielokrotne zaprzeczenie.