Lektion 6: Listor och tupler#
Moment: Mer systematisk genomgång av listor och tupler
Begrepp som introduceras: Listor och tupler
Arbetssätt: Arbeta gärna tillsammans med någon, men skriv egen kod. Diskutera med varandra! Försök svara på de frågor som har givna svar innan du tittar på svaret. Fråga handledarna om det är något svar du inte förstår!
Uppskattad arbetstid: 6 timmar.
Redovisning: Obligatorisk redovisning av uppgifterna enligt kursens hemsida.
Listor#
Vi har redan använt listor en del men i denna lektion ska vi gå igenom dem mer systematiskt.
Listor är ett av de viktigaste verktygen i Python. De innefattar bland annat det som i många andra språk kallas för arrayer, men är mer generella än dessa brukar vara.
En lista består av ett antal element av godtycklig datatyp.
Exempel:
[2, 3, 5, 7, 11]
är en lista med 5int
-objekt.['Eva', 'Olle', 'Lisa', 'Gustav']
är en lista med 4str
-objekt.[22, 'Päron', 4, 3.14159]
är en lista med 4 element av olika typer.[[1, 2, 3], 'Kalle', 25, ['x', 'y']]
är en lista med 4 element varav två är listor, ett är en sträng och ett är heltal.[]
är en lista med 0 element.
Konstruktion av listor#
En lista kan skapas genom att man, som i exemplen ovan, räknar upp elementen inom []-parenteser:
fib = [1, 1, 2, 3, 5, 8]
Elementen får index från 0 och uppåt. För att nå enskilda element kan man också använda index-värdet omgivet av []-parenteserna.
Operatorn =
samt metoderna append
och insert
kan användas för
att ändra listan.
Exempel |
Utskrift |
Kommentar |
---|---|---|
fib = [1, 1, 2, 3, 5, 8]
print(fib)
|
[1, 1, 2, 3, 5, 8] |
|
print(fib[3])
print(fib[2] + fib[5])
|
3 |
Åtkomst av enskilda element med hjälp av |
fib.append(12)
print(fib)
|
[1, 1, 2, 3, 5, 8, 12] |
Metoden |
fib[6] = 13
print(fib)
|
[1, 1, 2, 3, 5, 8, 13] |
Ett enskilt element kan ändras med tilldelningsoperatorn. |
fib.insert(0, 0)
print(fib)
|
[0, 1, 1, 2, 3, 5, 8, 13] |
|
Man kan använda negativa index för att referera till element i slutet av listan. Index -1 står för det sista, -2 för det näst sista, osv.
Exempel |
Utskrift |
---|---|
fib.append(fib[-1] + fib[-2])
print(fib)
|
[0, 1, 1, 2, 3, 5, 8, 13, 21] |
for i in range(3):
fib.append(fib[-1] + fib[-2])
print(fib)
|
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
|
Arbeta med listor#
Det finns flera standardfunktioner för listor, bl a: len
för antalet
element, max
och min
för största och minsta element, sum
för summan samt
sorted
som returnerar en ny lista som är sorterad. Alla dessa utom den
första kräver att elementen i listan är ”jämförbara”, t ex antingen
alla tal eller alla strängar.
Exempel |
Utskrift |
Kommentar |
---|---|---|
lst = [1, 3, 0.5, 4, 3.5, 8]
print(len(lst))
|
6 |
|
print(max(lst), min(lst), sum(lst))
|
8 0.5 20.0 |
|
print(sorted(lst))
print(lst)
|
[0.5, 1, 3, 3.5, 4, 8]
[1, 3, 0.5, 4, 3.5, 8]
|
Ursprungslistan oförändrad. |
print(sorted(['Ärlig',
'Eva',
'Åke']))
|
[’Eva’, ’Ärlig’, ’Åke’] |
Obs: Fel ordning på ’Å’ och ’Ä’. |
sorted(['x', 5])
|
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between
instances of 'int' and 'str'
|
Datatyperna |
Övningar#
Skriv en funktion
mean(lst)
som beräknar och returnerar medelvärdet av talen i listanlst
. Vad händer om listan innehåller strängar?Svar
def mean(lst): return sum(lst)/len(lst)
Skriv en funktion
median(lst)
som beräknar och returnerar medianen av talen i listanlst
. Medianen är det tal som kommer i mitten om man sorterar talen. Om antalet tal är jämnt så definierar vi det närmast över mitten som median.Svar
def median(lst): lst_sorted = sorted(lst) length = len(lst_sorted) return lst_sorted[length//2]
for
-satsen på listor#
För att iterera över elementen i en lista passar for
-satsen bra.
Exempel: Summera alla negativa element i en lista med tal.
lst = [-1, 3, 5, -3, 3, -8]
sum = 0
for x in lst:
if x < 0:
sum += x
print(sum) # Skriver -12
Övningar#
Skriv kod som, givet en lista med tal, konstruerar en ny lista utan de negativa talen.
Svar
lst = [-1, 3, 5, -3, 3, -8] new_lst = [] # Skapa en tom lista for x in lst: if x >= 0: new_lst.append(x) print(new_lst) # Skriver [3, 5, 3]
Skriv en funktion
between(lst, low, high)
som skapar och returnerar en lista av de element ilst
som ligger mellanlow
ochhigh
.Svar
def between(lst, low, high): result = [] for x in lst: if low <= x <= high: # OK att skriva så! result.append(x) return result l = [3, 1, 8, 19, 2, 5, 12] print(between(l, 3, 12)) # Skriver [3, 8, 5, 12]
Exempel: Värld med irrande paddor
I stället för två paddor som i föregående lektion kan vi ha en lista med
flera vimsiga paddor. Med användning av metoderna random_turtle
och
move_random
från föregående lektion:
# Create a set of dizzy turtles
turtles = []
for t in range(6):
turtles.append(random_turtle())
for i in range(1, 200):
for t in turtles:
move_random(t)
Du kan se hela programmet här. Ladda gärna ner och prova!
Övning#
Skapa en lista med 4 paddor som utplaceras i hörnen på en kvadrat. Rikta den första mot den andra, den andra mot den tredje, den tredje mot den fjärde och den fjärde mot den första. Så här:
Låt sedan paddorna gå ett steg (längd 5) mot den padda den är riktad mot. Uppdatera riktningarna så att de tittar mot sin målpaddas nya position. Så här kan det se ut efter en stund:
Avbryt körningen när padda 1 och 2 kommit inom 10 längdenheter från varandra.
Svar
import turtle def moveto(t, x, y): """Move to (x,y) without drawing""" t.penup() t.goto(x, y) t.pendown() def create_turtle(x, y, heading): """Create a turtle at (x,y) with a specified heading""" t = turtle.Turtle() moveto(t, x, y) t.setheading(heading) t.turtlesize(1.5) t.shape('turtle') return t ts = [create_turtle(-200, 200, 270), create_turtle(-200, -200, 0), create_turtle(200, -200, 90), create_turtle(200, 200, 180)] while ts[0].distance(ts[1]) > 10: for t in ts: t.forward(5) for i in range(4): j = (i + 1) % 4 # index for next neighbour ts[i].setheading(ts[i].towards(ts[j]))
Dellistor - ”skivning”#
Med hjälp av kolon kan man skapa dellistor. Detta kallas på engelska för ”slicing”, vilket vi översätter till skivning.
Skiva |
Betydelse |
---|---|
lst[m:n]
|
Delen av listan |
lst[:]
|
En ny lista med samma element som |
lst[m:]
|
Lista med alla element från och med index m. |
lst[:m]
|
Alla element från början till och med element med index m-1. |
Exempel: Antag att lst = [10, 11, 12, 13, 14, 15]
.
Kod |
Värde |
---|---|
lst[2:5]
|
[12, 13, 14] |
lst[:2]
|
[10, 11] |
lst[2:2]
|
[] |
lst[:]
|
[10, 11, 12, 13, 14, 15] |
lst[-3:]
|
[13, 14, 15] |
lst[-3:10]
|
[13, 14, 15] |
lst[-100:2]
|
[10, 11] |
Som synes går det bra att använda index-värden utanför gränserna i kolonuttrycken.
Det går också att ändra i listan både med tilldelningsoperatorn och med
del
-satsen
Kod |
Listans värde efteråt |
---|---|
lst
|
[10, 11, 12, 13, 14, 15] |
lst[1:3] = [21, 22, 23]
|
[10, 21, 22, 23, 13, 14, 15] |
del lst[2:4]
|
[10, 21, 13, 14, 15] |
lst[:2] = []
|
[13, 14, 15] |
Övningar#
Skriv en funktion
smooth(a)
som tar emot en listaa
med tal. Funktionen ska skapa och returnera en ny lista med samma antal element. Elementen på första och sista plats ska vara samma som ia
. I övrigt ska elementet på plats i vara medelvärdet ava[i-1]
,a[i]
ocha[i+1]
.Exempel:
print(smooth([1, 2, 6, 4, 5, 0]))
ska ge utskriften
[1, 3.0, 4.0, 5.0, 3.0, 0]
Svar
def smooth(a): res = [] res.append(a[0]) for i in range(1, len(a)-1): res.append(sum(a[i-1:i+2])/3) res.append(a[-1]) return res
Metoder och operatorer som ger information om listor#
Uttryck |
Värde |
Kommentar |
---|---|---|
a = [3, 9, 2, 7, 9, 2, 3]
|
||
a.count(9)
|
2 |
Räknar antal gånger som värdet |
a.count('a')
|
0 |
Värdet |
a.index(7)
|
3 |
|
a.index(9)
|
1 |
Om värdet förekommer flera gånger returneras index för första förekomsten |
a.index(9, 3)
|
4 |
Börjar leta på position 3. |
3 in a
|
True |
|
'x' in a
|
False |
|
'x' not in a
|
True |
|
a = [[1, 1], 1, [1, 2], [1, 1]]
|
||
a.count(1)
|
1 |
Räknar bara på ”topp”-nivå . |
a.count([1, 1])
|
2 |
|
a.index([1, 2])
|
2 |
Övningar#
Skriv en funktion
counter2(x, lst)
som räknar hur många gånger värdet x förekommer på nivå 2 i listan lst. Elementen i lst kan förutsättas vara listor.Exempel:
counter2(1, [[[1, 1]], [1, 2, 1], [1, 2]])
ska returnera 3, dvs den ska inte räkna ettorna i listans första element eftersom dessa finns i en sublista.Svar
def counter2(x, lst): count = 0 for lst_level_2 in lst: count += lst_level_2.count(x) return count
Ovanstående funktion förutsatte att elementen i listan är listor. Vad händer om de inte är det? Prova!
Med hjälp av standardfunktionen
type
kan man undersöka typen av ett visst värde. Exempelvis har uttryckettype(42) == int
värdetTrue
,type(42) == list
värdetFalse
ochtype(['a', 1, [2, 3]]) == list
värdetTrue
.Skriv funktionen
counter(x, lst)
som räknar förekomsten av x på både nivå 1 och 2.Exempel:
counter(1, [[[1, 1]], [1, 2, 1], 1, [1, 2], 1])
ska returnera 5.Svar
def counter(x, lst): count = 0 for element in lst: if type(element) == list: count += element.count(x) elif element == x: count += 1 return count
Metoder som ändrar listor#
Vi har tidigare sett hur enskilda list-element kan ändras med
tilldelningsoperatorn (typ a[3]='hej'
) och hur element kan läggas till
på slutet med metoden append
. Det finns flera andra metoder som kan
förändra en lista.
Kod |
Listans nya utseende |
Kommentar |
---|---|---|
a = [21, 11, 42, 17]
|
||
a.extend([0, 3])
|
[21, 11, 42, 17, 0, 3]
|
|
a.pop()
|
[21, 11, 42, 17, 0]
|
|
a.pop(1)
|
[21, 42, 17, 0]
|
|
a.insert(2, 47)
|
[21, 42, 47, 17, 0]
|
|
a.append(47)
|
[21, 42, 47, 17, 0, 47]
|
|
a.remove(47)
|
[21, 42, 17, 0, 47]
|
|
a.reverse()
|
[47, 0, 17, 42, 21]
|
|
a.sort()
|
[0, 17, 21, 42, 47]
|
|
a.clear()
|
[]
|
|
a = ['Ola', 'Bo', 'Mi']
|
['Ola', 'Bo', 'Mi']
|
|
a.sort()
|
['Bo', 'Mi', 'Ola']
|
|
a.append(3)
|
['Bo', 'Mi', 'Ola', 3]
|
|
a.sort()
|
TypeError: ’<’ not supported between instances of ’int’ and ’str’ |
Elementen måste vara jämförbara. |
a = [[2, 2, 1], [2, 1], [2, 3]]
|
[[2, 2, 1], [2, 1], [2, 3]]
|
|
a.sort()
|
[[2, 1], [2, 2, 1], [2, 3]]
|
Listelement är jämförbara |
Övningar#
Antag att
lst = ['a', 'b', 'c']
. Vad blir resultatet avlst.append([1, 2])
ochlst.extend([1, 2])
?Svar
Prova!
Skriv en funktion
extend(lst, x)
som gör samma sak somlst.extend(x)
utan att använda metodenextend
. Förutsätt attx
är en lista.Svar
def extend(lst, x): for element in x: lst.append(element)
Anmärkning: Eftersom listor är föränderliga objekt behövs ingen
return
-sats! Den lista som vår funktionextend
får som argument kommer vara förändrad efter anropet.Metoden
remove
tar bort första förekomsten av värdetx
på översta nivån. Skriv en funktionremove_all(lst, x)
som tar bort alla förekomster avx
på översta nivån i listanlst
.Svar
def remove_all(lst, x): while x in lst: lst.remove(x)
Anmärkning: Eftersom anropet
lst.remove(x)
börjar leta från början varje gång så är detta en långsam metod för långa listor.
Ett annat sätt att skapa listor#
En vanlig situation är att man vill bygga upp en ny lista utifrån en gammal. I flera tidigare exempel och övningar har vi sett mönstret
new_lst = []
for elem in old_lst:
if elem *uppfyller någon egenskap*:
new_lst.append[elem]
Det finns ett sätt som är både enklare och effektivare! För att göra en
ny lista som innehåller alla element från en lista lst
utom de med värdet
x kan vi skriva:
new_lst = [elem for elem in lst if elem != x]
Notera att denna kodrad åstadkommer ungefär samma sak som funktionen remove_all
ovan, med skillnaden att vi gör en ny lista istället för att ändra i den gamla. För att få samma effekt som anropet
remove_all(lst, x)
får vi alltså skriva
lst = [elem for elem in lst if elem != x]
Denna konstruktion kallas för ”list comprehension” på engelska. Vi kommer använda termen listbyggare på svenska.
Fler exempel med listbyggare:
Uttryck |
Värde |
---|---|
a = [x for x in range(7)]
|
[0, 1, 2, 3, 4, 5, 6] |
[z*z for z in a]
|
[0, 1, 4, 9, 16, 25, 36] |
[z*z for z in a if z % 2 == 0]
|
[0, 4, 16, 36] |
[round(z/3, 2) for z in a]
|
[0.0, 0.33, 0.67, 1.0, 1.33, 1.67, 2.0] |
Tupler#
Tupler liknar listor men de är ”immutable” (sv. oföränderliga), dvs går inte att ändra. Tupler skrivs med vanliga parenteser i stället för med hakparenteser. För övrigt använder man index-operatorn på samma sätt. Exempel:
Kod |
Värde |
Kommentar |
---|---|---|
t = (10, 11, 12, 13)
|
||
t[-1]
|
13 |
Indexering som i listor. |
t[0:3]
|
(10, 11, 12) |
Skivning som i listor. |
t.index(12)
|
2 |
Index för första förekomst. |
(42)
|
42 |
Ingen tupel! Vanligt int. |
(42,)
|
(42,) |
Tupel med ett element måste skrivas med kommatecken. |
()
|
() |
Tupel med noll element. |
t[-1] = 42
|
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object
does not support item assignment
|
Tupler kan inte ändras! |
'x', 'a', 'y'
|
('x', 'a', 'y')
|
Kan (ibland) skrivas utan parenteser. Se return-satsen från lösningen av andragradsekvationen! |
sorted(t, reverse=True)
|
13, 12, 11, 10 |
Standardfunktioner går bra |
Eftersom tupler är oföränderliga fungerar bara ett fåtal av listornas
metoder: copy
, count
och index
.
Man kan tycka att tupler inte tillför något utöver vad listor ger (allt som man kan göra med tupler kan man också göra med listor men inte tvärtom!). Motiveringen till deras existens är att hanteringen av dem kräver mindre resurser än vad listor gör. Det kommer exempel på användning av tupler i kommande lektioner.
Obligatoriska uppgifter
Skriv funktionerna
smooth_a(a, n)
ochsmooth_b(a, n)
som tar emot en listaa
med tal och ett icke-negativt heltaln
. Funktionerna ska skapa och returnera en ny listar
där\[ r_i = \frac{1}{2n+1}\sum \limits_{j=-n}^n a_{i+j} = \frac{1}{2n+1}\left( a_{i-n} + a_{i-n+1} + \cdots + a_{i+n} \right) \]dvs element nummer i i den nya listan
r
är medelvärdet av \(a_i\) och de \(2n\) omkringliggande talen.Om \(n = 1\) blir resultatet som i övningen med funktionen
smooth
tidigare i denna lektion, åtminstone för de inre elementen.För de element som ligger nära ändarna så att någon del av intervallet [i-n:i+n] är utanför listan går det inte att bilda medelvärdet enligt formeln ovan. Dessa punkter måste specialhanteras. Du ska implementera två olika strategier för detta:
strategi a):
Samma bredd (2n+1) på utjämningsoperatorn ska användas. För de element som svarar mot index mindre än 0 ska listans första värde användas. För de element som svarar mot index större än eller lika med listans längd ska listans sista element användas. Man kan se det som att listan utvidgas både uppåt och nedåt med n element som sätts lika med randelementet. Se figur och tips nedan.
strategi b):
Bara element som finns i listan ska användas. Nära listans ändar bildas medelvärdet därmed över färre tal. Se figur och tips nedan.
Tips: Använd funktionerna
min
ochmax
samt attmed
N = len(a) - 1
.Exempel: Koden
x = [1, 2, 6, 4, 5, 0, 1, 2] print('smooth_a(x, 1): ', smooth_a(x, 1)) print('smooth_a(x, 2): ', smooth_a(x, 2)) print('smooth_b(x, 1): ', smooth_b(x, 1)) print('smooth_b(x, 2): ', smooth_b(x, 2))
ska ge följande utskrifter
smooth_a(x, 1): [1.3333333333333333, 3.0, 4.0, 5.0, 3.0, 2.0, 1.0, 1.6666666666666667] smooth_a(x, 2): [2.2, 2.8, 3.6, 3.4, 3.2, 2.4, 2.0, 1.4] smooth_b(x, 1): [1.5, 3.0, 4.0, 5.0, 3.0, 2.0, 1.0, 1.5] smooth_b(x, 2): [3.0, 3.25, 3.6, 3.4, 3.2, 2.4, 2.0, 1.0]
Försök använda list-operatorer och listmetoder så mycket som möjligt! Inga
if
-satser behövs!Skriv funktionen
round_list(a_list, ndigits)
som returnerar en ny lista av talen ia_list
men där talen är avrundade tillndigits
decimaler.
Exempel: Kodenprint('smooth_a(x, 1) rounded: ', round_list(smooth_a(x, 1), 2))
ska, med samma värde på
x
som ovan, ge utskriftensmooth_a(x, 1) rounded: [1.33, 3.0, 4.0, 5.0, 3.0, 2.0, 1.0, 1.67]
Tips: Använd funktionen
round
och ”list comprehension”!