Operatorer
Kategori | Operatorer |
---|---|
Aritmetikk |
**
|
*
/
//
%
|
|
+
-
|
|
Relasjoner |
==
!=
<
<=
>
>=
in
not in
is
is not
|
Logikk |
not
|
and
|
|
or
|
|
Betingelse |
if … else
|
Aritmetikk med tall
Symbolene +
-
*
/
**
utfører henholdsvis addisjon, subtraksjon, multiplikasjon, divisjon og eksponentiering med to tall.
Alle operatorene fungerer likt for både heltall (int) og flyttall (float), men merk at divisjon /
alltid returnerer et flyttall, selv om resultatet numerisk sett er et heltall.
print(6 + 2) # 8
print(6 - 2) # 4
print(6 * 2) # 12
print(6 / 2) # 3.0
print(6 ** 2) # 36
print(36 ** 0.5) # 6.0 (en eksponent på 0.5 er det samme som kvadratrot)
Aritmetikk med strenger
# Strenger repeteres flere ganger med gangesymbol (*) og et heltall
print('bar' * 2) # barbar
# Strenger konkateneres (limes sammen) med pluss (+)
a = 'foo'
b = 'bar'
c = a + b
print(c) # foobar
Heltallsdivisjon og modulo
Heltallsdivisjon med restverdi er den første formen for divisjon vi lærte på barneskolen. La oss si at du skal fordele 14 gullmynter på 4 pirater: da kan hver pirat få 3 gullmynter, og så blir det 2 gullmynter til overs. Vi kan uttrykke regnestykket i Python ved å benytte operatorene for heltallsdivisjon //
og modulo %
slik:
coins = 14
pirates = 4
coins_per_pirate = coins // pirates
remainder = coins % pirates
print(coins, 'gullmynter skal fordeles på', pirates, 'sjørøvere.')
print('Hver sjørøver får da', coins_per_pirate, 'gullmynter',
'og det blir', remainder, 'mynter til overs.')
Heltallsdivisjon er som vanlig divisjon, men runder alltid nedover (mot negativ uendelig).
print('Operatøren / utfører vanlig divisjon')
print(' 7 / 4 =', (7/4)) # 1.75
print()
print('Operatøren // utfører heltallsdivisjon:')
print(' 7 // 4 =', ( 7//4)) # 1 (runder nedover)
print('-1 // 4 =', (-1//4)) # -1 (runder også nedover)
print('-7 // 4 =', (-7//4)) # -2 (runder også nedover)
print('Når nevneren er negativ')
print(' 7 // -4 =', (7//-4)) # -2 (runder også nedover)
print('-7 // -4 =', (-7//-4)) # 1 (runder altså alltid nedover uansett)
Modulo-operatoren %
returnerer «resten» etter heltallsdivisjon. Tallet sier noe om hvor langt unna dividenden er å være delelig med divisoren; hvor mye man får til overs. Vi kan bruke dette for å avgjøre om et tall er delelig med et annet (hvis resten er 0, er det delelig).
print(8 % 3) # 2
print(7 % 3) # 1
print(6 % 3) # 0 6 er delelig med 3, derfor må 6 % 3 være 0
print(5 % 3) # 2
print(4 % 3) # 1
print(3 % 3) # 0 3 er delelig med 3
print(2 % 3) # 2
print(1 % 3) # 1
print(0 % 3) # 0 0 er delelig med 3
print(-1 % 3) # 2
print(-2 % 3) # 1
print(-3 % 3) # 0 -3 er delelig med 3
print(-4 % 3) # 2
print(-5 % 3) # 1
print(-6 % 3) # 0 -6 er delelig med 3
En vanlig bruk av modulo er å finne siste siffer i et tall:
print(123 % 10) # 3
print(1234 % 10) # 4
print(12345 % 10) # 5
Eller avgjøre om et tall er oddetall eller partall. Partall er som kjent delelig med 2:
num = 42
is_num_even = (num % 2) == 0
print(f'{num} er et partall: {is_num_even}')
En annen vanlig bruk av modulo er for å få en verdi til å «gå i ring» innenfor et visst intervall. For eksempel, la oss si at vi ønsker at x-koordinatet til en ball alltid skal befinne seg innenfor et vindu med bredde på 400 piksler; hvis ballen faller utenfor, vil vi at den skal komme tilbake på den andre siden av vinduet. Da kan vi bruke modulo-operatoren til å «wrappe» verdien tilbake til intervallet dersom den skulle komme utenfor:
x = 385
...
x = (x + 10) % 400
print(x) # 395 (som forventet når vi gjør 385 + 10)
...
x = (x + 10) % 400
print(x) # tilbake til 5 i stedet for 405, fordi 405 % 400 blir 5
Sammenligning av verdier
Relasjons-operatorene benyttes for å sammenligne to verdier, og resulterer alltid i en boolsk verdi (enten True
eller False
).
print('== sjekker om to verdier er like')
print(2 == 2) # True
print(2 == 3) # False
print(2 == 2.0) # True
print('foo' == 'foo') # True
print('foo' == 'bar') # False
print()
print('!= sjekker om to verdier er ulike')
print(2 != 2) # False
print(2 != 3) # True
print(2 != 2.0) # False
print('foo' != "foo") # False
print('foo' != 'bar') # True
print('< sjekker om venstre side er «mindre enn» høyre side')
print(2 < 3) # True
print(2 < 2) # False
print(2 < 1) # False
print('foo' < 'barbar') # False (sammenligner «ASCII-alfabetisk»)
print('foo' < 'foo') # False
print('barbar' < 'foo') # True
print()
print('<= sjekker om venstre side er «mindre enn eller lik» høyre side')
print(2 <= 3) # True
print(2 <= 2) # True
print(2 <= 1) # False
print('foo' <= 'barbar') # False (sammenligner «ASCII-alfabetisk»)
print('foo' <= 'foo') # True
print('barbar' <= 'foo') # True
Operatorene >
og >=
fungerer likt som <=
og <
bare med motsatt resultat.
Flyttall og avrundingsfeil
Se også videoen Floating point numbers av Computerphile.
print(0.1 + 0.1 == 0.2) # True, men...
print(0.1 + 0.1 + 0.1 == 0.3) # False!
print(0.1 + 0.1 + 0.1) # gir 0.30000000000000004 (oj sann!)
print((0.1 + 0.1 + 0.1) - 0.3) # gir 5.55111512313e-17 (lite, men ikke 0!)
Derfor: Ikke bruk ==
for å sammenligne flyttall! Sjekk i stedet at de to tallene som sammenlignes er nesten like.
def almost_equals(a, b):
epsilon = 0.0000000001
return abs(a - b) < epsilon # abs()-funksjonen gir absolutt-verdien
print(0.1 + 0.1 + 0.1 == 0.3) # False
print(almost_equals(0.1 + 0.1 + 0.1, 0.3)) # True
Medlemskap
Operatorene in
og not in
brukes for å sjekke om en verdi er medlem av en liste, tuple, mengde, streng eller en annen samling av ting.
# Sjekk om symboler finnes i strenger
print('a' in 'abc') # True
print('d' in 'abc') # False
print('A' in 'abc') # False ('A' og 'a' er forskjellige symboler)
print('a' not in 'abc') # False
print('d' not in 'abc') # True
print()
print('bc' in 'abc') # True ('bc' utgjør en sammenhengende del av 'abc')
print('ac' in 'abc') # False (selv om både 'a' og 'c' er i 'abc')
# Sjekk om en verdi finnes i en liste eller ikke
print(1 in [1, 2, 3]) # True
print(4 in [1, 2, 3]) # False
print(1 not in [1, 2, 3]) # False
print(4 not in [1, 2, 3]) # True
# Sjekk om en verdi finnes i en tuple eller ikke
print(1 in (1, 2, 3)) # True
print(4 in (1, 2, 3)) # False
print(1 not in (1, 2, 3)) # False
print(4 not in (1, 2, 3)) # True
# Sjekk om en verdi finnes i en mengde eller ikke
print(1 in {1, 2, 3}) # True
print(4 in {1, 2, 3}) # False
print(1 not in {1, 2, 3}) # False
print(4 not in {1, 2, 3}) # True
Logiske operatorer
For å bruke boolske verdier (True og False) i logiske uttrykk, bruker vi boolske operatorer. Disse operatorene er konjunksjon and
, disjunksjon or
og negasjon not
.
x |
not x |
---|---|
False |
True |
True |
False |
x |
y |
x or y |
x and y |
---|---|---|---|
False |
False |
False |
False |
False |
True |
True |
False |
True |
False |
True |
False |
True |
True |
True |
True |
Uttrykk som består av boolske uttrykk og logiske operatorer er en del av hva vi kaller «boolsk algebra». Bruken av and
og or
i slik logikk minner om det vi er vant til fra dagligtalen. Legg merke til at svaret på et spørsmål som «er du voksen eller er du barn?» alltid vil være True
(med mindre du verken voksen eller barn, så klart; bare da er svaret False
).
print('and returnerer True hvis begge leddene er True')
print(True and True) # True
print(True and False) # False
print(False and True) # False
print(False and False) # False
print()
print('or returnerer True hvis minst ett av leddene er True')
print(True or True) # True
print(True or False) # True
print(False or True) # True
print(False or False) # False
print()
print('not returnerer motsatt boolsk verdi')
print(not True) # False
print(not False) # True
Det er vanlig å benytte logiske operatorer for å binde sammen flere uttrykk som hver for seg evaluerer til boolske verdier.
age = 2000
if (age < 0) or (age > 130):
print(f'Litt vanskelig å tro at du er {age} år gammel...')
Uttrykk med betingelse (if else)
# Eksempel på uttrykk med betingelse
age = 20
status = 'et barn' if age < 18 else 'en voksen'
print(f'Alder: {age}. Dette er {status}.') # Alder: 20. Dette er en voksen.
# Generelt
condition = True # or False
value_if_true = 'a'
value_if_false = 'b'
result = value_if_true if condition else value_if_false
print(result) # a
Presedens og assosiativitet
En vanlig feil er at man gjør gale antakelser om hvilken rekkefølge operatorer i et større uttrykk utføres i; hvordan de «usynlige parentesene» i uttrykket er plassert.
For aritmetikk gjelder de samme presedens-reglene som er vanlig i matematikk.
print('Presedens:')
print(2 + 3 * 4) # gir 14, ikke 20 ( * har høyere presedens enn + )
print(5 + 4 % 3) # gir 6, ikke 0 ( % har høyere presedens enn + )
print(2 ** 3 * 4) # gir 32, ikke 4096 (** har høyere presedens enn * )
Presedenstabell
Vi gir her en oversikt over noen operatorer rangert fra høyeste til laveste presedens. Parenteser vil alltid overstyre presedens og assosiativitet, og er derfor øverst på listen. Alle operatører untatt **
og relasjonene assosierer venstre-til-høyre dersom det er aktuelt.
Operatorer | |
---|---|
() |
Parentes. Det som er mellom parentesene evalueres før en operator utenfor parentesen. |
** |
Eksponentiering. Assosierer høyre-til-venstre. |
* / // % |
Multiplikasjon, divisjon og modulo. |
+ - |
Addisjon og subtraksjon. |
< <= > >= == != in not in is is not |
Relasjoner. Disse vil ikke assosiere verken til høyre eller venstre; dersom man har flere slike etter hverandre vil de komponeres som en konjunksjon i stedet. For eksempel, -1 < 0 == False gir det samme svaret som (-1 < 0) and (0 == False) , og altså ikke det samme som (-1 < 0) == False slik man ellers kunne trodd. |
not |
Logisk negasjon. |
and |
Logisk konjunksjon. |
or |
Logisk disjunksjon. |
if … else |
Betinget verdi. |
I presedenstabellen over (og også i tabellen øverst på denne siden) er operatorene sortert etter presedens. Det vil si, **
har høyeste presedens mens if … else
har den laveste. Operatorer i samme rad har samme presedens (for eksempel har +
og -
samme presedens).
Det er operatorene med høyest presedens som utføres «først» med mindre parenteser indikerer noe annet.
Assosiativitet
Dersom flere operasjoner med samme presedens forekommer i samme uttrykk, utføres de som hovedregel fra venstre til høyre.
print("Assosiativitet: venstre til høyre")
print(5 - 4 - 3) # det samme som (5 - 4) - 3, altså -2 (ikke 4)
print(9 // 3 // 3) # det samme som (9 // 3) // 3, altså 1 (ikke 9)
Det finnes likevel noen unntak:
**
er høyre-assosiativ (det vil si at2 ** 3 ** 4
er det samme som2 ** (3 ** 4)
)- Relasjonene (altså
==
!=
<
<=
>
>=
in
not in
is
is not
) assosierer hverken til høyre eller til venstre; dersom man har flere slike i et uttrykk vil de komponeres som en konjunksjon i stedet. For eksempel,-1 < 0 == False
vil tolkes som(-1 < 0) and (0 == False)
, og altså ikke som(-1 < 0) == False
.
Parenteser
Parenteser vil alltid overstyre presedens og assosiativitet. Det er god stil å bruke parenteser for å vise hvilken rekkefølge du ønsker at operatorene utføres i, selv om det ikke alltid er nødvendig – det gjør koden din mer lesbar og mindre utsatt for feil som skyldes at du ikke husker presedenstabellen.
Eksempler
Under er det noen eksempler hvor det er fort gjort å feiltolke hvordan uttrykket evalueres fordi det ikke er angitt parenteser. Bruk reglene for presedens og assosiativitet kombinert med presedenstabellen og prøv å forutsi hva hvert uttrykk evaluerer til før du kjører koden og ser fasiten.
print(True or True and False)
print(not False or True)
print(not (False or True))
print()
print(2 < 3 < 4)
print(not 3 < 2 < 1)
print(not 3 < 2 and 2 < 1)
print()
print('b' in 'box')
print('b' in 'box' == True) # (krasjer i nettleseren; men prøv på egen maskin)
print('a' and 'b' in 'box')
print('a' or 'z' in 'box')
Moralen i historien: benytt parenteser for å vise hva du mener. Det er fort gjort å huske feil rekkefølge, og du kan heller ikke forvente at dine kolleger og ditt fremtidige jeg (som senere skal vedlikeholde koden) husker den.