Boolske uttrykk
- Boolske verdier er også verdier
- Evaluering steg for steg
- Logikk og sannhetstabeller
- Kortslutning
- Truthy og falsy verdier
- Hva and og or egentlig returnerer
- Vanlige feil og misforståelser
- Boolske verdier som tall
- Funksjonene any og all
- God stil
- Eksempler
Boolske verdier er også verdier
Typen bool har nøyaktig to verdier: True og False. Dette er verdier på samme måte som 42 er en verdi av typen int og "hei" er en verdi av typen str. Når vi skriver True direkte i koden, er det en literal, akkurat som når vi skriver 42 eller "hei".
print(type(42)) # <class 'int'>
print(type("hei")) # <class 'str'>
print(type(True)) # <class 'bool'>
print(type(False)) # <class 'bool'>
Merk at True og False skrives med stor forbokstav og uten anførselstegn. "True" er en streng, ikke en boolsk verdi, og true (med liten t) er ikke noe som finnes, eller eventuelt et veldig forvirrende variabelnavn.
En sammenligning som age >= 18 er et uttrykk, på samme måte som age + 1 er et uttrykk. Forskjellen er bare hvilken type verdi uttrykket evaluerer til: age + 1 evaluerer til en int, mens age >= 18 evaluerer til en bool.
age = 20
print(age + 1) # 21
print(age >= 18) # True
Siden boolske verdier er helt vanlige verdier, kan vi gjøre det samme med dem som med andre verdier. Vi kan for eksempel lagre dem i variabler, skrive dem ut, legge dem i lister, og bruke dem som argument til og returverdi fra funksjoner.
age = 20
is_adult = age >= 18 # Variabelen is_adult får verdien True
print(is_adult) # True
answers = [True, False, age > 100]
print(answers) # [True, False, False]
if is_adult: # Betingelsen er variabelen is_adult, som er True
print("Velkommen inn!")
En betingelse i en if-setning trenger altså ikke å være en sammenligning. Det kan være ethvert uttrykk, inkludert en variabel, en literal eller et funksjonskall. Python evaluerer uttrykket, og ser deretter på hvilken verdi det ble.
def is_even(n):
return n % 2 == 0 # Returnerer resultatet av uttrykket: True eller False
print(is_even(4)) # True
print(is_even(7)) # False
x = 10
if is_even(x):
print(x, "er et partall")
Evaluering steg for steg
Et boolsk uttrykk evalueres på samme måte som et aritmetisk uttrykk: ett deluttrykk av gangen, og hvert deluttrykk erstattes med verdien det evaluerer til. Når vi regner ut 2 + 3 * 4 regner vi først ut 3 * 4 og erstatter det med 12, slik at vi får 2 + 12, og deretter 14.
Tilsvarende for et boolsk uttrykk:
x = 3
(x < 5) and not (x == 3)
# Steg 1: evaluer x < 5 -> True and not (x == 3)
# Steg 2: evaluer x == 3 -> True and not True
# Steg 3: evaluer not True -> True and False
# Steg 4: evaluer and -> False
Hvilken rekkefølge operatorene utføres i bestemmes av presedens (se presedenstabellen). For de logiske operatorene gjelder:
- Aritmetikk (
+,*osv.) utføres før relasjoner (<,==,inosv.) - Relasjoner utføres før
not notutføres førandandutføres føror
Dette betyr at uttrykket under har «usynlige parenteser» slik som vist i kommentarene:
x = 7
print(x > 5 or x < 0 and not x == 7)
# Med usynlige parenteser:
# (x > 5) or ((x < 0) and (not (x == 7)))
# Steg for steg:
# True or (False and (not True))
# True or (False and False)
# True or False
# True
Legg merke til at and binder sterkere enn or, på samme måte som * binder sterkere enn +. Uttrykket True or True and False betyr derfor True or (True and False), og ikke (True or True) and False.
print(True or True and False) # True
print((True or True) and False) # False
Selv om det ikke alltid er nødvendig, er det god stil å bruke parenteser for å gjøre det tydelig hva man mener.
Kjedede sammenligninger
I Python kan vi kjede sammen flere sammenligninger, slik vi er vant med fra matematikk. Et uttrykk som 0 < x < 10 tolkes som (0 < x) and (x < 10).
x = 5
print(0 < x < 10) # True
print((0 < x) and (x < 10)) # True (det samme)
x = 15
print(0 < x < 10) # False
Logikk og sannhetstabeller
Operatorene not, and og or er i logikken definert ved sine sannhetstabeller (se også logiske operatorer). En sannhetstabell viser resultatet for hver mulige kombinasjon av verdier.
x |
not x |
|---|---|
False |
True |
True |
False |
x |
y |
x and y |
x or y |
|---|---|---|---|
False |
False |
False |
False |
False |
True |
False |
True |
True |
False |
False |
True |
True |
True |
True |
True |
Kort oppsummert:
x and yerTruebare hvis begge erTrue.x or yerTruehvis minst én erTrue.not xer det motsatte avx.
Sannhetstabeller for sammensatte uttrykk
Vi kan lage sannhetstabeller for større uttrykk ved å regne ut én kolonne for hvert deluttrykk. La oss se på uttrykket not x or y. Siden not har høyere presedens enn or, betyr dette (not x) or y.
x |
y |
not x |
(not x) or y |
|---|---|---|---|
False |
False |
True |
True |
False |
True |
True |
True |
True |
False |
False |
False |
True |
True |
False |
True |
De Morgans lover
To uttrykk er ekvivalente dersom de har samme sannhetstabell. To nyttige ekvivalenser er De Morgans lover:
not (x and y)er det samme som(not x) or (not y)not (x or y)er det samme som(not x) and (not y)
Med andre ord: når vi flytter en not inn i en parentes, må vi bytte and med or og omvendt. Dette er nyttig for å gjøre negerte betingelser enklere å lese.
Norsk har et eget ord for den andre loven: «verken … eller». «Jeg drikker verken kaffe eller te» betyr «det er ikke sant at jeg drikker kaffe eller te», altså not (coffee or tea). Det betyr det samme som «jeg drikker ikke kaffe, og jeg drikker ikke te», altså (not coffee) and (not tea).
age = 10
# Disse to betingelsene er ekvivalente
if not (age >= 18 and age <= 67):
print("Utenfor arbeidsfør alder")
if age < 18 or age > 67:
print("Utenfor arbeidsfør alder")
Kortslutning
Når Python evaluerer x and y, evalueres x først. Hvis x er False, er svaret uansett False, uavhengig av hva y er. Python gidder da ikke å evaluere y i det hele tatt. Tilsvarende for x or y: hvis x er True, er svaret uansett True, og y blir ikke evaluert. Dette kalles kortslutning (engelsk: short-circuit evaluation).
False and ...blirFalseuten at høyresiden evalueres.True or ...blirTrueuten at høyresiden evalueres.
Vi kan se at høyresiden ikke blir evaluert ved å la høyresiden være noe som ville krasjet programmet, for eksempel en divisjon med null:
print(False and 1/0) # False (1/0 blir aldri evaluert)
print(True or 1/0) # True (1/0 blir aldri evaluert)
print(True and 1/0) # Krasjer med ZeroDivisionError
Vi kan også se det ved å bruke funksjoner som skriver ut noe når de kalles:
def yes():
print(" yes() ble kalt")
return True
def no():
print(" no() ble kalt")
return False
print("no() and yes():")
print(no() and yes()) # Bare no() blir kalt
print()
print("yes() or no():")
print(yes() or no()) # Bare yes() blir kalt
print()
print("yes() and no():")
print(yes() and no()) # Begge blir kalt
Kortslutning som vakt
Kortslutning er nyttig for å beskytte et uttrykk som bare gir mening dersom en annen betingelse er oppfylt. Venstresiden fungerer da som en vakt for høyresiden.
x = 0
y = 5
# Hvis x er 0, blir y / x aldri evaluert, og programmet krasjer ikke
if x != 0 and y / x > 2:
print("y er mer enn dobbelt så stor som x")
else:
print("Kan ikke dele på 0, eller y er ikke stor nok")
names = []
# Hvis listen er tom, blir names[0] aldri evaluert (som ville gitt IndexError)
if len(names) > 0 and names[0] == "Ada":
print("Ada er først i køen")
else:
print("Ada er ikke først i køen")
Rekkefølgen er viktig! Dersom vi bytter om på venstre- og høyresiden, krasjer programmet:
names = []
if names[0] == "Ada" and len(names) > 0: # Krasjer med IndexError
print("Ada er først i køen")
Truthy og falsy verdier
En betingelse i en if-setning trenger ikke å evaluere til en boolsk verdi. Dersom verdien er av en annen type, vil Python likevel tolke den som enten True eller False. Verdier som tolkes som True kalles truthy, mens verdier som tolkes som False kalles falsy.
if "Hello":
print("Strengen 'Hello' er truthy")
if 0:
print("Dette skrives ikke ut, for 0 er falsy")
if [1, 2, 3]:
print("En liste med elementer er truthy")
if []:
print("Dette skrives ikke ut, for en tom liste er falsy")
Funksjonen bool konverterer en verdi til den boolske verdien den tolkes som:
print(bool(42)) # True
print(bool(-1)) # True
print(bool(0)) # False
print(bool(0.0)) # False
print()
print(bool("hei")) # True
print(bool(" ")) # True (et mellomrom er ikke tomt)
print(bool("0")) # True (en streng med tegnet 0 er ikke tom)
print(bool("False")) # True (en streng med ordet False er ikke tom)
print(bool("")) # False
print()
print(bool([1, 2])) # True
print(bool([0])) # True (listen har ett element, selv om det er 0)
print(bool([False])) # True
print(bool([])) # False
print()
print(bool(None)) # False
Verdier som er falsy er:
FalseNone0og0.0(tallverdien null)""(den tomme strengen)[](den tomme listen), og tomme samlinger av andre typer, for eksempel{}ogset()
Nesten alle andre verdier er truthy. En god tommelfingerregel er at «null» og «tomt» er falsy, mens alt annet er truthy.
Operatoren not fungerer også med truthy og falsy verdier, og returnerer alltid en boolsk verdi:
print(not 0) # True
print(not 42) # False
print(not "") # True
print(not "hei") # False
print(not []) # True
En vanlig bruk av truthy og falsy er å sjekke om en liste eller streng er tom:
names = []
if not names:
print("Listen er tom")
name = input("Hva heter du? ")
if name:
print(f"Hei, {name}!")
else:
print("Du skrev ikke noe navn")
Hva and og or egentlig returnerer
Så langt har vi sett på and og or med boolske verdier. Men hva skjer om vi bruker dem med andre verdier?
print(0 or 5) # 5
print(3 or 5) # 3
print(0 and 5) # 0
print(3 and 5) # 5
print("" or "tom") # tom
Resultatet er verken True eller False, men en av de opprinnelige verdiene! For å forstå hvorfor, må vi kombinere det vi vet om kortslutning og truthy/falsy verdier. Python evaluerer venstresiden først. Dersom venstresiden alene avgjør svaret, returneres venstresiden. Ellers er det høyresiden som avgjør svaret, og da returneres høyresiden.
x and y: Hvisxer falsy, returneresx. Ellers returneresy.x or y: Hvisxer truthy, returneresx. Ellers returneresy.
Med andre ord returnerer and og or alltid enten venstresiden eller høyresiden, uendret. Det er ikke nødvendigvis en boolsk verdi. Dersom vi bruker resultatet som en betingelse, vil det tolkes som truthy eller falsy, og da får vi akkurat samme oppførsel som i sannhetstabellen. Derfor merker vi vanligvis ikke dette.
Vi kan uttrykke det samme med if-else -uttrykk:
x and y # gir det samme som: x if not x else y
x or y # gir det samme som: x if x else y
La oss se på noen eksempler steg for steg:
0 and 5
# 0 er falsy, så svaret er avgjort; venstresiden returneres
# -> 0
3 and 5
# 3 er truthy, så høyresiden avgjør svaret; høyresiden returneres
# -> 5
0 or "" or "standard"
# or assosierer venstre til høyre: (0 or "") or "standard"
# Steg 1: evaluer parentesen (0 or "")
# 0 er falsy, så høyresiden i parentesen, "", returneres
# -> "" or "standard"
# Steg 2: evaluer "" or "standard"
# "" er falsy, så høyresiden, "standard", returneres
# -> "standard"
print(0 and 5) # 0
print(3 and 5) # 5
print(0 or "" or "standard") # standard
print([] or [1, 2]) # [1, 2]
print([1] and []) # []
print(None or 0) # 0
print(True and "hei") # hei
print(False or None) # None
Dette blir noen ganger brukt for å angi en standardverdi:
name = input("Hva heter du? ")
name = name or "anonym" # Hvis name er tom streng, blir name "anonym"
print(f"Hei, {name}!")
Selv om dette er lov, er det ofte tydeligere å skrive det som et if-else -uttrykk, slik at leseren ikke trenger å kjenne til disse reglene (se God stil).
Merk at not er et unntak: not returnerer alltid True eller False.
Vanlige feil og misforståelser
Sammenligne med flere verdier
En vanlig feil er å skrive en betingelse slik vi ville sagt det i dagligtale: «hvis svaret er ja eller Ja».
answer = "nei"
if answer == "ja" or "Ja": # Feil!
print("Du svarte ja") # Skrives ut uansett hva answer er
Med presedens og parenteser blir betingelsen (answer == "ja") or "Ja". Når answer er "nei", blir dette False or "Ja", som evaluerer til "Ja". Det er en streng som ikke er tom, og derfor truthy. Betingelsen er altså truthy uansett hva answer er.
answer = "nei"
print(answer == "ja" or "Ja") # Ja
Det riktige er å skrive ut begge sammenligningene, eller å bruke in med en liste:
answer = "nei"
if answer == "ja" or answer == "Ja":
print("Du svarte ja")
if answer in ["ja", "Ja"]:
print("Du svarte ja")
Sjekke om en verdi er innenfor et intervall
if x > 0 and < 10: # SyntaxError: begge sider av and må være hele uttrykk
...
Det riktige er x > 0 and x < 10, eller kortere 0 < x < 10.
Strengen “True” er ikke True
True og "True" er to forskjellige verdier av forskjellige typer. Dette er spesielt lett å blande når man leser input fra brukeren, siden input() alltid returnerer en streng.
answer = input("Skriv True eller False: ")
print(answer == True) # False, uansett hva du skrev (en str er aldri lik en bool)
print(bool(answer)) # True, så lenge du skrev noe (også om du skrev False)
print(answer == "True") # True bare hvis du skrev nøyaktig True
Boolske verdier som tall
I Python er bool egentlig en spesialisert variant av int: True oppfører seg som tallet 1, og False oppfører seg som tallet 0.
print(True + True) # 2
print(True * 10) # 10
print(False + 5) # 5
print(True == 1) # True
print(False == 0) # True
Dette kan brukes til å telle hvor mange verdier i en liste som er True:
answers = [True, False, True, True, False]
print(sum(answers)) # 3
Vær likevel forsiktig med å bruke dette for å være smart (se God stil).
Funksjonene any og all
Funksjonene any og all tar en liste som argument, og fungerer som or og and over alle elementene i listen:
any(a)returnererTruedersom minst ett element iaer truthy.all(a)returnererTruedersom alle elementene iaer truthy.
I motsetning til and og or returnerer any og all alltid True eller False.
print(any([False, False, True])) # True
print(any([False, False, False])) # False
print(all([True, True, True])) # True
print(all([True, False, True])) # False
print()
print(any([0, "", "hei"])) # True ("hei" er truthy)
print(all([1, "a", [0]])) # True (alle er truthy)
scores = [7, 9, 4, 10]
passed = []
for score in scores:
passed.append(score >= 5)
print(passed) # [True, True, False, True]
print(all(passed)) # False (ikke alle bestod)
print(any(passed)) # True (minst én bestod)
print(sum(passed)) # 3 (antall som bestod)
En litt overraskende konsekvens av definisjonen er hva som skjer med en tom liste. any([]) er False, siden det ikke finnes noe element som er truthy. all([]) er True, siden det ikke finnes noe element som ikke er truthy.
print(any([])) # False
print(all([])) # True
God stil
Merk at dette avsnittet omhandler stil og ikke korrekthet. Koden til venstre fungerer, men koden til høyre er lettere å lese.
Unødvendig sammenligning med True/False: Siden en sammenligning allerede evaluerer til True eller False, er det unødvendig å sammenligne resultatet med True en gang til.
|
|
Returnere True/False med en if-setning: Når en funksjon skal returnere om noe er sant eller ikke, er det fristende å bruke en if-setning. Men uttrykket i betingelsen er allerede den verdien vi ønsker å returnere.
|
|
Tilordne True/False med en if-setning: Det samme gjelder for variabler.
|
|
Negert sammenligning: Det finnes en sammenligningsoperator for det motsatte av hver sammenligning, så vi trenger ikke not.
|
|
Intervall: En kjedet sammenligning ligner på matematisk notasjon og er lettere å lese.
|
|
Bruk av or for å velge en verdi: Dette er lov, men leseren må kjenne til hva or egentlig returnerer for å forstå koden.
|
|
Fancy bruk av boolske verdier som tall:
|
|
Eksempler
Prøv å forutsi hva hvert uttrykk evaluerer til før du kjører koden og ser fasiten. Bruk reglene for presedens, kortslutning og truthy/falsy verdier.
print(True and not False)
print(not True or True)
print(not (True or True))
print(3 > 2 > 1)
print(1 < 3 == 3)
print()
print(0 or 7)
print(7 and 0)
print("" or "tom")
print([] and [1, 2])
print(not "")
print(None or 0)
print()
print(1 == 1 or 2)
print(1 == 2 or 2)
print("a" == "b" or "c")
print(True + True + False)
print(all([1, "a", [0]]))
print(any([0, "", []]))
