Lister
- Opprette lister
- Funksjoner og operasjoner
- Indeksering og beskjæring
- Mutasjon og alias
- Kopiering av lister
- Destruktive funksjoner
- Leting etter elementer
- Legge til elementer
- Fjerne elementer
- Løkker over lister
- Sortering og reversering
- Pakke ut en liste i variabler
- Tupler
- Listeforståelse (løkker inni lister)
- Konvertering mellom lister og strenger (split/join)
Opprette lister
# To standard måter å opprette lister på
a = []
b = list()
print(type(a), len(a), a)
print(type(b), len(b), b)
print(a == b)
# Liste med ett element
a = ["foo"]
b = [42]
print(type(a), len(a), a)
print(type(b), len(b), b)
print(a == b)
# Lister med flere elementer
a = [2, 3, 5, 7, 11]
b = list(range(5))
c = ["foo", 42, True, None, ""]
print(type(a), len(a), a)
print(type(b), len(b), b)
print(type(c), len(c), c)
# Lister med variabelt antall elementer
a = ["foo"] * 5
b = list(range(5))
print(type(a), len(a), a)
print(type(b), len(b), b)
Funksjoner og operasjoner
a = [2, 3, 5, 2]
print("a = ", a)
print("len =", len(a))
print("min =", min(a))
print("max =", max(a))
print("sum =", sum(a))
# Et par forskjellige lister
a = [2, 3, 5, 3, 7]
b = [2, 3, 5, 3, 7] # lik til a
c = [2, 3, 5, 3, 8] # forskjellig fra a
d = [2, 3, 5] # prefix for a
print("a =", a)
print("b =", b)
print("c =", c)
print("d =", d)
print("------------------")
print("a == b", (a == b))
print("a == c", (a == c))
print("a != b", (a != b))
print("a != c", (a != c))
print("------------------")
print("a < c", (a < c)) # Sammenligning basert på første ulike element
print("d < a", (d < a)) # Prefiksen er "mindre enn"
# Konkatenering
a = [3, 4]
b = [8, 9]
print("a =", a)
print("b =", b)
print("a + b =", a + b)
# Repetisjon
print("a * 3 = ", a * 3)
Indeksering og beskjæring
# Indeksering og beskjæring fungerer på samme måte som for strenger
a = [2, 3, 5, 7, 11, 13]
print("a =", a)
# Indeksering. Første indeks er 0.
print("a[0] =", a[0])
print("a[2] =", a[2])
# Negative indekser
print("a[-1] =", a[-1])
print("a[-3] =", a[-3])
# Beskjæring a[start:slutt] eller a[start:slutt:steg]
print("a[0:2] =", a[0:2])
print("a[1:4] =", a[1:4])
print("a[1:6:2] =", a[1:6:2])
Mutasjon og alias
I motsetning til datatyper vi har sett hittil, kan vi endre på en liste uten å opprette en ny verdi i minnet. Dette kaller vi å mutere listen.
# Opprett en liste
a = [2, 3, 4]
# La b være en variabel som referer til samme liste som a
# Siden a og b er to variabler som refererer til det samme muterbare
# objekt, kaller vi a og b for aliaser.
b = a
# Mutasjon (endring) av listen
a[0] = 99
b[1] = 42
print(a)
print(b)
Dersom to variabler refererer til samme muterbare objekt, kalles de for aliaser. Funksjonsparametre er eksempler på aliaser.
# Når en funksjon muterer en liste via et alias har funksjonen en
# sideeffekt
def f(a):
a[0] = 42
a = [2, 3, 5, 7]
print(a)
f(a)
print(a)
print("---")
# Alias kan bli brutt ved å endre variabelen
def foo(a):
a[0] = 99
a = [5, 2, 0] # aliaset blir brutt her
a[0] = 42
a = [3, 2, 1]
print(a)
foo(a)
print(a)
# Opprett en liste
a = [2, 3, 5, 7]
# Opprett et alias for listen
b = a
# Opprett en ny liste med de samme elementene
c = [2, 3, 5, 7]
# a og b er referanser til (/aliaser for) DEN SAMME listen
# c er en referanse til en annen, men LIK liste
print("først:")
print(" a == b:", a == b)
print(" a == c:", a == c) # == -operatoren sier om to verdier er LIKE
print(" a is b:", a is b) # is -operatoren sier om to verdier er DEN SAMME
print(" a is c:", a is c)
# Mutasjon av a endrer også b (DEN SAMME listen) men ikke c (en annen liste)
a[0] = 42
print("etter mutasjonen a[0] = 42")
print(" a =", a)
print(" b =", b)
print(" c =", c)
print(" a == b:", a==b)
print(" a == c:", a==c)
print(" a is b:", a is b)
print(" a is c:", a is c)
Kopiering av lister
# Vi må være forsiktig ved kopiering av lister, slik at vi ikke muterer en
# liste gjenomm et alias av vanvare.
import copy
a = [2, 3]
# To kopier
b = a # Ikke en kopi, bare et alias
c = copy.copy(a) # Ekte kopi
# I begynnelsen ser kopiene tilforlatelig like ut
print("Først...")
print(" a =", a)
print(" b =", b)
print(" c =", c)
# Så muterer vi a[0]
a[0] = 42
print("Etter mutasjonen a[0] = 42")
print(" a =", a)
print(" b =", b)
print(" c =", c)
Andre måter å kopiere
import copy
a = [2, 3]
b = copy.copy(a)
c = a[:]
d = a + []
e = list(a)
f = a.copy()
*g, = a
h = []
for element in a:
h.append(element)
a[0] = 42
print(a, b, c, d, e, f, g, h)
Destruktive funksjoner
En funksjon er destruktiv dersom den har sideeffekter som muterer en liste.
# En destruktiv funksjon er skrevet for å mutere en liste. Den trenger ikke
# returnere noe, siden den som kaller også har et alias til listen.
def fill(a, value):
for i in range(len(a)):
a[i] = value
a = [1, 2, 3, 4, 5]
print("Først, a =", a)
fill(a, 42)
print("Etter fill(a, 42), a =", a)
En ikke-destruktiv funksjon vil ikke ha sideeffekter, og vi benytter oss av returverdien i stedet.
import copy
def destructive_remove_all(a, value):
while value in a:
a.remove(value)
def non_destructive_remove_all(a, value):
# Vanligvis skriver vi ikke-destruktive funksjoner ved å opprette
# en ny liste fra scratch, og jobber på den
result = []
for element in a:
if element != value:
result.append(element)
return result # ikke-destruktive funksjoner MÅ returnere svaret!
def alternate_non_destructive_remove_all(a, value):
# Vi kan også skrive en ikke-destruktiv funksjon ved å først bryte
# aliaset, og deretter benytte en destruktiv tilnærming
a = copy.copy(a)
destructive_remove_all(a, value)
return a # ikke-destruktive funksjoner må uansett returnere!
a = [1, 2, 3, 4, 3, 2, 1]
print("Først")
print(" a =", a)
destructive_remove_all(a, 2)
print("Etter destructive_remove_all(a, 2)")
print(" a =", a)
b = non_destructive_remove_all(a, 3)
print("Etter b = non_destructive_remove_all(a, 3)")
print(" a =", a)
print(" b =", b)
c = alternate_non_destructive_remove_all(a, 1)
print("Etter c = alternate_non_destructive_remove_all(a, 1)")
print(" a =", a)
print(" c =", c)
Leting etter elementer
# Inneholder listen min verdi?
a = [2, 3, 5, 2, 6, 2, 2, 7]
print("a =", a)
print("2 in a =", (2 in a))
print("4 in a =", (4 in a))
# eller ikke?
print("2 not in a =", (2 not in a))
print("4 not in a =", (4 not in a))
# Hvor mange ganger opptrer min verdi?
a = [2, 3, 5, 2, 6, 2, 2, 7]
print("a =", a)
print("a.count(1) =", a.count(1))
print("a.count(2) =", a.count(2))
print("a.count(3) =", a.count(3))
# Hvor i listen befinner verdien seg, da?
# a.index(element) eller a.index(element, start)
a = [2, 3, 5, 2, 6, 2, 2, 7]
print("a =", a)
print("a.index(6) =", a.index(6))
print("a.index(2) =", a.index(2))
print("a.index(2,1) =", a.index(2,1))
print("a.index(2,4) =", a.index(2,4))
# Oj! Krasjer dersom elmentet ikke er der
a = [2, 3, 5, 2]
print("a =", a)
print("a.index(9) =", a.index(9)) # krasjer!
print("Vi kom visst ikke så langt...")
# Løsning: benytt (element in liste) først.
a = [2, 3, 5, 2]
print("a =", a)
if (9 in a):
print("a.index(9) =", a.index(9))
else:
print("9 er ikke der", a)
print("Hurra!")
Legge til elementer
Destruktive metoder for å legge til elementer:
# Vi oppretter en liste og gir den et alias. Alle endringer vi gjør her
# reflekteres i aliaset, hvilket betyr at endringene er destruktive
a = [2, 3]
alias = a
# Legg til på slutten med .append
a.append(7)
print(a) # [2, 3, 7]
# Legg til på en bestemt posisjon med .insert
a.insert(2, 42)
print(a) # [2, 3, 42, 7]
# Utvid listen med flere elementer på en gang med .extend eller '+='
b = [100, 200]
a.extend(b)
print(a) # [2, 3, 42, 7, 100, 200]
a += b
print(a) # [2, 3, 42, 7, 100, 200, 100, 200]
print()
print(alias) # [2, 3, 42, 7, 100, 200, 100, 200]
Ikke-destruktive operasjoner for å legge til elementer:
a = [2, 3]
# Legg til på slutten med +
b = a + [13, 17]
print(a)
print(b)
# Legg til midt inne i listen med beskjæring
c = a[:1] + [42] + a[1:]
print(a)
print(c)
Destruktiv vs. ikke-destruktiv utvidelse
print("Destruktiv:")
a = [2, 3]
b = a # lager alias
a += [4]
print(a)
print(b)
print("Ikke-destruktiv:")
a = [2, 3]
b = a # lager alias
a = a + [4] # bryter aliaset med b, a er nå referanse til ny liste
print(a)
print(b)
Fjerne elementer
Destruktive metoder for å fjerne elementer
a = [2, 3, 5, 3, 7, 6, 5, 11, 13]
print("a =", a)
# Fjerne første opptreden av et bestemt element
a.remove(5)
print("Etter a.remove(5), a=", a)
a.remove(5)
print("Etter enda en a.remove(5), a=", a)
# Fjerne det siste elementet i listen
item = a.pop()
print("Etter item = a.pop()")
print(" item =", item)
print(" a =", a)
# Fjerne et element på en bestemt indeks
item = a.pop(3)
print("Etter item = a.pop(3)")
print(" item =", item)
print(" a =", a)
Ikke-destruktive operasjoner for å fjerne elementer
a = [2, 3, 5, 3, 7, 5, 11, 13]
print("a =", a)
# Ikke-destruktiv fjerning av elementene mellom indeks 2 og 3
b = a[:2] + a[3:]
print("Etter b = a[:2] + a[3:]")
print(" a =", a)
print(" b =", b)
Løkker over lister
# Iterasjon med indeks
a = [2, 3, 5, 7]
for index in range(len(a)):
print(f"a[{index}] =", a[index])
print("---")
for index, item in enumerate(a):
print(f"a[{index}] =", item)
# Iterasjon uten indeks, såkalt for-hver -løkke (engelsk: foreach)
# Lister og strenger er begge samlinger, såkalte "itererbare" typer.
# Det betyr at vi kan benytte en for-løkke på dem direkte
a = [2, 3, 5, 7]
for item in a:
print(item)
# IKKE FJERN ELLER LEGG TIL ELEMENTER TIL SAMME LISTE DU GÅR GJENNOM
# MED EN FOR-LØKKE! INDEKSER KRØLLER SEG TIL!(dette er ikke et problem
# for strenger, siden de ikke kan muteres)
a = [2, 3, 5, 3, 7]
print("a =", a)
# Mislykket forsøk på å fjerne alle 3'erne
for index in range(len(a)):
if (a[index] == 3): # vi krasjer her etter en stund
a.pop(index)
print("Hit kommer vi ikke")
# IKKE MUTER EN LISTE INNI EN FOR-HVER -LØKKE!
# Vil ikke krasje, men gjør heller ikke som vi forventer
a = [3, 3, 2, 3, 4]
print("a =", a)
# Mislykket forsøk på å fjerne alle 3'erne
def should_be_removed(x):
return x == 3
for item in a:
if should_be_removed(item):
a.remove(item)
print(a)
# Bedre: mutering i en while-løkke.
# Her har vi full kontroll på hvordan indeks endrer seg.
a = [2, 3, 5, 3, 7]
print("a =", a)
# Vellykket forsøk på å fjerne alle 3'erne
index = 0
while (index < len(a)):
if (a[index] == 3):
a.pop(index)
else:
index += 1
print("Huzza! a =", a)
Sortering og reversering
Destruktiv sortering og reversering
# Sortering
a = [7, 2, 5, 3, 5, 11, 7]
print("Først, a =", a)
a.sort()
print("Etter a.sort(), a =",a)
print("---")
# Reversering
a = [2, 3, 5, 7]
print("Først, a =", a)
a.reverse()
print("Etter a.reverse(), a =", a)
Ikke-destruktiv sortering og reversering
# Sortering
a = [7, 2, 5, 3, 5, 11, 7]
print("Først, a =", a)
b = sorted(a)
print("Etter b = sorted(a)")
print(" a =", a)
print(" b =", b)
print("---")
# Reversering
a = [2, 3, 5, 7]
print("Først, a =", a)
b = reversed(a)
c = list(reversed(a))
print("Etter b = reversed(a) og c = list(reversed(a))")
print(" a =", a)
print(" b =", b)
print(" c =", c)
print("Her er elementene i b:")
for x in b:
print(x, end=" ")
print()
print("Her er elementene i b en gang til (men hæ???):")
for x in b:
print(x, end=" ")
print()
print("---")
Pakke ut en liste i variabler
Gitt en liste kan du “pakke den ut” i variabler.
a = ["Florida", 15.4, "2022-09-16"]
place, temp, date = a
print(f"{place = }", f" {temp = }", f" {date = }", sep="\n")
Hvis listen er lang, kan du pakke opp kun de par første verdiene og la resten bli en ny liste. Operasjonen er ikke-destruktiv.
a = ["Florida", 15.2, 13.5, 17.2, 13.6, 14.2]
place, *temps = a
print(f"{a=}")
print(f"{place=}")
print(f"{temps=}")
eller de par første og de par siste. Variabelen med *
foran plukker opp resten i en ny liste.
a = ["Florida", "not interested", 15.2, 13.5, 17.2, 13.6, 14.2, "OK"]
place, _, *temps, last_temp, status = a
print(f"{a = }")
print(f"{place = }")
print(f"{_ = }")
print(f"{temps = }")
print(f"{last_temp = }")
print(f"{status = }")
Ved å sette *
foran en liste, kan vi pakke opp elementene i listen og bruke dem som om vi bare skilte verdiene med komma uten at de var i en liste. For eksempel kan vi bruke elementene som argumenter til et funksjonskall.
def add(x, y):
return x + y
a = [2, 2]
result = add(*a)
print(result)
Eller vi kan kombinere to lister ikke-destruktivt:
a = [1, 2]
b = [3, 4]
c1 = [a, b] # En liste av lister -- en 2-dimensjonell liste
c2 = [*a, *b] # En liste med verdiene fra to lister -- en "flat" liste
print(a, b, c1, c2, sep="\n")
En funksjon kan akseptere et ukjent antall argumenter ved å pakke dem inn i en liste
def multiply(*nums):
# nums er en liste som inneholder alle argumentene
result = 1
for num in nums:
result *= num
return result
print(multiply(2, 2))
print(multiply(2, 2, 3))
Eller kreve et minimums antall argumenter ved å bare pakke inn bare de siste argumentene i en liste.
def multiply(first_num, second_num, *rest):
result = first_num * second_num
for num in rest:
result *= num
return result
print(multiply(2, 2, 3))
print(multiply(2, 2))
print(multiply(2)) # krasjer, ikke nok argumenter
Tupler
En tuple er en slags liste som ikke kan muteres.
t = (1, 2, 3)
print(type(t), len(t), t)
a = [1, 2, 3]
t = tuple(a)
print(type(t), len(t), t)
# Tupler kan ikke muteres
t = (1, 2, 3)
print(t[0])
t[0] = 42 # Krasj!
print(t[0])
# Parallell tilording av verdier
(x, y) = (1, 2)
print(x)
print(y)
# Paralell tilordning er nyttig for bytting av verdier
(x, y) = (y, x)
print(x)
print(y)
# Tuple med kun ett element
t = (42)
print(type(t), t*5) # oj
t = (42,) # bruk komma for å lage en tuple
print(type(t), t*5)
Tupler fungerer på samme måte som lister, men de kan ikke muteres. For å endre på tupler må man bruke ikke-destruktive funksjoner. Ikke-destruktive funksjoner for lister og for tupler er de samme og pleier å ha identisk syntaks.
En vanlig bruk av tupler er å returnere flere verdier fra samme funksjon:
# Bruk en tuple til å returnere flere verdier
def positive_and_negative_of(x):
return (x, -x)
# Pakk ut resultatet til flere variabler (variablene skilles med ,)
hi, lo = positive_and_negative_of(5)
print(f"{hi} {lo}")
# Behold resultatet som en tuple (én variabel på venstresiden av =)
hilo = positive_and_negative_of(7)
print(f"{hilo}")
Listeforståelse (løkker inni lister)
I Python er det mulig å opprette lister med en løkke. Dette kalles for list comprehension på engelsk (listeforståelse) og det kalles dette fordi det er en kompakt og relativt forståelig måte å beskrive innholdet som skal være i listen man oppretter.
# Den lange måten
a = []
for i in range(10):
a.append(i + 1)
print(a)
# Med listeforståelse
a = [i + 1 for i in range(10)]
print(a)
# For de ambisiøse: listeforståelse med betingelser
a = [i + 1 for i in range(20) if i % 2 == 0]
print(a)
# Listeforståelse for å ikke-destruktivt filtrere en liste
def divisible_by_3(x):
return x % 3 == 0
b = [x for x in a if divisible_by_3(x)]
print(b)
# Listeforståelse for å ikke-destruktivt anvende en funksjon på
# hvert element i en liste
def square(x):
return x * x
c = [square(x) for x in b]
print(c)
Konvertering mellom lister og strenger (split/join)
# bruk list(s) for å konvertere en streng til liste med tegn
a = list("hurra!")
print(a) # ['h', 'u', 'r', 'r', 'a', '!']
# bruk s1.split(s2) for å konvertere en streng s1 til en liste
# med strenger, klippet opp langs s2'er inne i s1
a = "Hva holder du på med?".split(" ")
print(a) # ['Hva', 'holder', 'du', 'på', 'med?']
# bruk "".join(a) for å lime sammen/konkatenere en liste med strenger
print("".join(a)) # Hvaholderdupåmed?
# s.join(a) for å lime sammen med s som lime-streng
print(" ".join(a)) # Hva holder du på med?
print("--".join(a)) # Hva--holder--du--på--med?