Göm menyn

Strängar och listor

Sammansatta datatyper

Alla datatyper vi hittils har studerat sparar ett enstaka värde (ett heltal, ett logiskt värde etc.). Vill vi spara flera värden kan vi så klart använda flera variabler. Ibland är den lösningen bra, men ibland har den stora nackdelar. Några exempel är:

  • Ofta behöver man lagra många värden som "hör ihop". Om man till exempel behöver lagra de namn som har namnsdag under årets alla dagar, är det jobbigt att behöva ha hundratals olika variabler för detta, och det är väldigt lätt att något blir fel.

  • Ofta behöver man utföra samma operation på många olika värden som hör ihop. Om man till exempel skriver en funktion som tar in ett namn och talar om när detta namn har namnsdag, är det jobbigt att skriva en if-sats med hundratals olika grenar för att testa ett namn/datum i taget.

  • Ofta är antalet "sammanhörande" värden inte ens konstant. Om man ska kunna läsa in en lista på kursdeltagare från en fil, vet man inte från början om det finns 10, 100 eller 200 kursdeltagare. Även om man skulle skapa 200 olika variabler kanske man någon gång råkar ut för en kurs som har 250 deltagare -- för att inte tala om hur jobbig koden blir att skriva.

För att lösa dessa problem, och flera andra, används sammansatta datatyper. De är datatyper som inte bara innehåller ett värde, som t.ex. talet 12, utan flera olika värden som på ett eller annat sätt kan plockas ut. I det här och nästa kapitel kommer vi studera fyra vanliga sammansatta datatyper: listor, strängar, tupler och dictionaries.

List

En lista är en ordnad sekvens av data som oftast är av samma eller liknande datatyp, till exempel en lista av tal, eller en lista av strängar. Detta är dock inget krav i python: Varje element i en lista kan ha sin egen datatyp.

Med "ordnad" menar vi att varje element har en position, ett index. Vi kan alltså prata om det första eller andra eller sista elementet i en lista (till skillnad från en mängd, som också innehåller element men inte har någon ordning).

Antalet element i en lista kan variera och man kan när som helst ta bort eller lägga till värden.

Listor finns i de flesta språk, och i de språk som inte har dem som standard kan man oftast själv implementera listor på ett eller annat sätt. Vissa språk har både listor och arrayer, en datatyp som liknar listor men som ibland har lite annorlunda egenskaper -- här kan olika språk tyvärr lägga lite olika betydelser i orden. Men just nu kommer vi enbart att fokusera på listor!

En lista kan definieras genom att värdena i listan räknas upp inom [] och är separerade med komma (,).

Exempel:

>>> a = [42, 'fyrtiotvå', 4.2]

En lista som inte innehåller några element kallas tomma listan och betecknas [].

Dokumentation

Utökning av listor

Element kan dynamiskt läggas till och tas bort från en lista på många olika sätt.

Det vanligaste är att lägga till ett element på slutet av listan vilket antingen görs med metoden append, som lägger till ett element, eller operatorn +=, som lägger till elementen från en annan lista:

>>> a = [1,2]
>>> a += [3, 4]
>>> a
[1, 2, 3, 4]
>>> a.append(5)
>>> a
[1, 2, 3, 4, 5]

Även borttagning av ett element görs oftast på slutet av en lista, då med funktionen pop

>>> a.pop()
5
>>> a
[1, 2, 3, 4]

Exemplet ovan visar även att metoden pop returnerar elementet som togs bort

Indexering av listor

Som vi har sett ovan så består listor av ett antal element. För att hämta ut ett specifikt element i en given lista så använda vi oss av indexering. Med indexering kan vi peka ut ett specifikt element i listan utifrån dess position i listan.

En viktig sak att komma ihåg är att Python, likt de flesta programmeringspråk, använder sig av nollbaserad numrering. Det första elementet har index 0 och inte 1 som man kan tänka sig.

lista[N] är elementet på plats N. Detta kan vi använda både för att hämta ut ett värde och för att byta ut ett värde på en position, som i exemplet nedan.

>>> a = [1,2,3]
>>> a[0]
1
>>> a[1] = "två"
>>> a
[1,"två",3]

Eftersom en listas längd kan ändras är det bra att kunna ta reda på längden:

>>> a = [1,2,3,5,7]
>>> len(a)
5

En lista har ju bara element på positioner 0, 1, 2, och så vidare. Vad händer om vi försöker komma åt elementet på position -1? I många språk tolkas detta bokstavligen, och vi får någon typ av fel i och med att position -1 "inte finns". I Python har man istället valt att låta -1 stå för det sista elementet i listan, -2 för det näst sista, och så vidare. Det gör att man enklare kan indexera från slutet utan att själv behöva titta på listans längd:

>>> a = [1,2,3,5,7]
>>> a[len(a)-1]
7
>>> a[-1]  # Lite mindre att skriva
7
>>> a[len(a)-2]
5
>>> a[-2]
5

Slicing

För att hämta ut en delsekvens av elementen i listan använder vi slicing.

lista[start:slut] skapar en dellista från start till elementet före slut, där både start och slut är frivilliga argument. Varför "före slut"? Detta är en vanlig konvention i många språk, och det gör t.ex. att lista[0:10] + lista[10:20] == lista[0:20] utan att man får med element 10 två gånger.

lista[start:slut:steg] skapar en dellista från start till elementet *före slut med intervallet steg ("vart steg:te element"), där både start, slut och steg är frivilliga argument.

Här kommer lite exempel på hur man kan använda slicing för att få ut delsekvenser ur listor:

>>> rainbow = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
>>> rainbow[2]
'yellow'
>>> rainbow[2:5]
['yellow', 'green', 'blue']
>>> rainbow[-1]
'violet'
>>> rainbow[-3]
'blue'
>>> rainbow[2:]
['yellow', 'green', 'blue', 'indigo', 'violet']
>>> rainbow[:2]
['red', 'orange']
>>> rainbow[2:6:2]
['yellow', 'blue']
>>> [rainbow[1]] + ['apple', 'banana']
['orange', 'apple', 'banana']

Iteration över listor

En lista kan lämna ut iteratorer till den som vill iterera över listan. Därför kan en for-loop gå igenom alla element i en lista på följande sätt:

>>> for element in [12, 'spam', 2.18]:
...     print(element)
...
12
spam
2.18

Fler listoperationer

Man kan göra mycket roligt med listor, så här kommer lite andra exempel på hur man kan använda dem:

>>> a = ['one', 'two', 'three']
>>> a[1] = 2
>>> a
['one', 2, 'three']
>>> len(a)
3

Konkatenering

Med additions-operatorn kan du konkatenera två listor. Detta returnerar en ny lista. Till exempel:

>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]

Strängar

En väldigt vanlig sekvens av data som används inom programmering är strängar (engelska string), som är sekvenser av tecken.

Eftersom strängar är så vanliga har nästan alla programmeringsspråk en dedikerad datatyp för strängar och python är inget undantag.

Som vi redan sett tidigare skapas strängar genom att omge tecknen som ska ingå i strängen med enkel- eller dubbelcitattecken " '. Vid utskrift får man alltid enkelcitattecken (apostrof).

>>> 'Spam and Eggs'
'Spam and Eggs'
>>> "Spam and Eggs"
'Spam and Eggs'

Specialtecken

Vad händer om man vill ha en sträng som innehåller tecknet ' eller "? Om man bara behöver ett av dem kan man använda det andra tecknet för att omge strängen:

>>> 'I want to "quote" something'

Ibland vill man dock ha en sträng som innehåller båda. För det måste man kunna markera vilka citattecken som är del av strängen och vilket som avslutar den.

Detta görs genom att lägga till ett backslash \ innan de tecken som ingår i strängen:

>>> print("I want to \"quote\" something`)
I want to "quote" something

Tecken som börjar med ett backslash kallas för escape characters eller "escapetecken", och används inte bara för citattecken. Den vanligaste användningen av escape characters är radbrytningar vilket uttrycks med \n.

Eftersom \ nu är upptaget för att markera att nästa tecken betyder något annat kan \ inte skrivas på vanligt sätt. För att komma runt det används \\

>>> s = 'Spam\nEggs'
>>> s
'Spam\nEggs'
>>> print(s)
Spam
Eggs
>>> s = "\\program files\\python"
>>> print(s)
\program files\python

Dokumentation

Indexering och slicing

Precis som med listor kan vi indexera och plocka ut delar av strängar.

  • 'sträng[N]' hämtar ut elementet på plats N. N kan även vara negativt, där -1 är det sista tecknet i strängen.
  • 'sträng[start:slut]' skapar en delsträng från start till elementet precis före slut, där både start och slut är frivilliga argument.
  • 'sträng[start:slut:steg]' skapar en delsträng från start till elementet precis före slut med intervallet steg, där både start, slut och steg är frivilliga argument.
>>> s = 'Spamalot'
>>> s[1:]
'pamalot'
>>> s[1:3]
'pa'
>>> s[:5]
'Spama'
>>> s[:-1]
'Spamalo'
>>> s[:]
'Spamalot'

Jämförelser med strängar

Vi kan även göra jämförelser med strängar på samma sätt som vi har sett med siffror. När vi till exempel använder operatorerna < och > så är det bokstavsordningen som används:

>>> "Aardvark" < "Abbot"
True
>>> "Queen" > "King"
True
>>> "a" * 4 == "aaaa"
True

För att vara mer exakt används lexikografisk ordning enligt en teckenkodning som inte alltid stämmer överens med våra intuitioner. Till exempel kommer stora bokstäver före små, och våra svenska tecken har inte den ordning som man kunde önska (då får man använda andra metoder för jämförelse, men det är överkurs).

>>> 'X' < 'a'
True
>>> 'ä' < 'å'
True

Vi kan även söka efter delsträngar i en annan sträng:

>>> "a" in "The Holy Grail"
True
>>> "holy" in "The Holy Grail"  # litet eller stort H
False
>>> "vers" in "universitet"
True

Operationer på strängar

Vad kan vi göra med våra strängar då, det vill säga vilka operationer kan vi använda oss av?

Operation Vad händer?
s + s Konkatenering (sammanslagning)
s * 4 Upprepning
s[3] Indexering
s[1:3] Slicing (ta ut delsträngar)
len(s) Längden av strängen
for c in s: ... Iterera över tecknen i strängen

Exempel 1 - Byta ut tecken

Vi vill ha en funktion change som tar en sträng och byter ut alla förekomster av ett visst tecken mot ett annat (genom att skapa en ny sträng givetvis). Det ska fungera så här: Spamalot

>>> change("gurkburk", "u", "ö")
'görkbörk'
>>> change("gräset växer", "ä", "e")
"greset vexer"

I kod kan vi uttrycka det så här:

def change(string, changefrom, changeto):
    """ Returns a string where all occurences of changefrom is replaced by changeto """
    result = ""
    for char in string:
        if char == changefrom:
            result = result + changeto
        else:
            result = result + char
    return result

Idén bakom funktionen är att iterera över strängen, tecken för tecken, och jämföra varje tecken med tecknet som ska ersättas. Om de är samma lägger vill till ersättningstecknet på slutet av vår resultatsträng. Annars lägger vi det nuvarande tecknet på resultatsträngen.

Exempel 2 - Hitta en delsträng

Vi vill ha en funktion locate som kollar om en delsträng (eng. substring) finns någonstans i en längre sträng. Om så är fallet returneras indexet till den första förekomsten av delsträngen i strängen, annars returneras -1.

>>> locate("Spam and eggs","egg")
9
>>> locate("TEAM","I")
-1

Hur kan vi implementera detta? Innan vi skriver kod kan det vara bra att se hur vi kan ta oss an problemet.

Spamalot forts.

När vi har en klar bild över hur vi vill ha det kan vi implementera det i kod.

def locate(string, sub):
    """ Returns the index of sub if found, otherwise return -1 """
    s = len(string)
    p = len(sub)
    for i in range(s-p+1):
        if string[i:i+p] == sub:
            return i
    return -1
>>> locate("Spam and eggs","egg")
9
>>> locate("TEAM","I")
-1

Lagring av strängar

En sträng är i grunden en form av lista med tal, där varje tal tolkas som ett tecken. Funktionen ord (eng. ordinal) tar en bokstav och returnerar det tal som lagras. På samma sätt tar funktionen chr (eng. char) ett heltal och returnerar den bokstav som siffran kodar.

>>> ord('a')
97
>>> chr(87)
'W'

Olika teckenkodningar

En teckenkodning är ett sätt att representera en grupp av tecken i form av olika koder. Här kommer några av de vanligaste:

  • ASCII (American Standard Code for Information Interchange) från 1963 är en teckenkodning som tilldelar talen 0-127 olika tecken, men som saknar t.ex. svenska bokstäver.
  • ISO 8859-1 (även kallad Latin-1) är en utökning som tilldelar talen 0-255 olika tecken och som inkluderar svenska och många andra europeiska tecken.
  • Unicode är den mest aktuella världstandarden med målet att inkludera alla världens skrivtecken.

Tillhörande quiz

Finnes här


Sidansvarig: Peter Dalenius
Senast uppdaterad: 2021-12-03