Lektion 6: Listor och tupler
Denna lektion innehåller Obligatorisk uppgift 2 (OU2)
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! Tips: Använd debuggern |
Uppskattad arbetstid: | Schemalagd handledningstid inkl redovisning: 6 timmar. Utöver det räkna med eget arbete med lika många timmar. |
Redovisning: | Obligatorisk redovisning av uppgifterna enligt kursens hemsida.
Notera att den pythonkod du redovisar skall följa kodningsreglerna Speciellt:
|
Backup: | När du arbetar på egen dator måste du se till att det löpande görs backup på de filer (py-filer) som du skapar under kursens gång. Om du har filerna enbart lagrade på din dator finns risk att allt försvinner vid ett datorhaveri eller vid stöld. |
Samarbete, fusk och plagiat: |
|
Listor
Vi har redan använt listor en del men i denna lektion ska vi gå igenom dem mer systematiskt.Detta är ett av de viktigaste verktygen i Python med ett stort användningsområde. Det innefattar bland annat det som i många andra språk kallas för arrayer men är mer generellt ä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 10 | Åtkomst av enskilda element m h a [ ]. Första index är alltid 0. |
fib.append(12) print(fib) | [1, 1, 2, 3, 5, 8, 12] |
Metoden append lägger till ett element sist.
|
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] |
insert( index, värde)
skjuter in ett nytt värde på angivet index.
|
fib.pop(4) print(fib) | [0, 1, 1, 2, 5, 8, 13] |
pop( index)
tar bort element på angivet index.
|
Man kan använda negativa index för att referera element i slutet av listan. Index -1 står för det sista, -2 för det näst sista etc.
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, 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 |
---|---|---|
m = [1, 3, 0.5, 4, 3.5, 8] print(len(m)) | 6 | |
print(max(m), min(m), sum(m)) | 8 0.5 20.0 | |
print(sorted(m)) print(m) | [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 int och str är inte jämförbara.
|
Övningar
-
Skriv en funktion
mean(m)
som beräknar och returnerar medelvärdet av talen i listanm
! Vad händer om listan innehåller strängar?def mean(m): return sum(m)/len(m)
-
Skriv en funktion
median(m)
som beräknar och returnerar medianen av talen i listanm
! 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.def median(m): l = sorted(m) return l[len(l)//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.
Övningar
-
Givet en lista med tal. Skriv kod som konstruerar en ny lista utan de negativa talen.
m = [-1, 3, 5, -3, 3, -8] n = [] # Skapa en tom lista for x in m: if x >= 0: n.append(x) print(n) # Skriver [3, 5, 3]
-
Skriv en funktionen
between(lista, low, high)
som skapar och returnerar en lista av de element ilista
som ligger mellanlow
ochhigh
.def between(lista, low, high): result = [] for x in lista: 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 metodernarandom_turtle
och move_random
från föregående
lektion:
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 de möts i mitten.
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 för "nästa" padda ts[i].setheading(ts[i].towards(ts[j]))
En program med hjälpfunktionen
create_turtle
finns här.
Dellistor — "skivning"
Med hjälp av kolon kan man skapa dellistor. Detta kallas på engelska för "slicing".Skiva | Betydelse |
---|---|
lst[m:n] |
Delen av listan lst som har index från m till n - 1.
|
lst[:] |
En ny lista med samma element som lst .
|
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. |
lst[m:n:2] |
Delen av listan lst som har index från m till n - 1, varannat element
|
lst[m:n:s] |
Delen av listan lst som har index från m till n - 1, var s:te element
|
lst[m:n:-1] |
Delen av listan lst som har index från m till n - 1, i bakvänd ordning. Notera m > n.
|
Exempel
Antag att listan 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(x)
som tar emot en lista 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 ix
medan element på plats i ska vara medelvärdet av avx[i-1]
,x[i]
ochx[i+1]
.Exempel:
print(smooth([1, 2, 6, 4, 5, 0]))
[1, 3.0, 4.0, 5.0, 3.0, 0]
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 | |
a.count('a') | 0 | |
a.index(7) | 3 | |
a.index(9) | 1 | |
a.index(9, 3) | 4 | Börjar leta i position 3. |
3 in a | True | |
'x' in a | False | |
'x' not in a | True | |
a = [[1, 1], 1, [1, 2], [1, 1]] | Innehåller fyra element: [1, 1], 1, [1, 2], [1, 1] | |
a.count(1) | 1 | Räknar bara på "topp"-nivå, dvs nivå 1 . |
a.count([1, 1]) | 2 | Räknar bara på nivå 2. |
a.index([1, 2]) | 2 | Räknar bara på nivå 1. |
Övningar
-
Skriv en funktion
counter2(x, lista)
som räknar hur många gånger värdet x förekommer på nivå 2 i listan lista. Elementen i lista 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.def counter2(x, lista): res = 0 for lst in lista: # Iterera för varje element lst res += lst.count(x) # lst.count(x) räknar antal x return res
- 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, lista)
som räknar förekomsten x på både nivå 1 och 2.Exempel:
counter(1, [[[1, 1]], [1, 2, 1], 1, [1, 2], 1])
ska returnera 5.def counter(x, lista): res = 0 for lst in lista: #Iterera för varje element lst if type(lst) == list: #Om elementet är av typen list? res += lst.count(x) #Räkna antal x i listan lst elif lst == x: #Annars om lst är x res += 1 return res
Metoder som ändrar listor
Vi har tidigare sett dels hur enskilda list-element kan ändras med tilldelningsoperatorn (typa[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 list.
Kod | Listans nya innehåll | Kommentar |
---|---|---|
a = [21, 11, 42, 17] | ||
a.extend([0, 3]) | [21, 11, 42, 17, 0, 3] | |
a.pop() | [21, 11, 42, 17, 0] | pop returnerar 3. |
a.pop(1) | [21, 42, 17, 0] | pop returnerar 11. |
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
-
Skapa en lista
my_list
med 10 slumpade heltal i intervallet [0, 10]. Skriv sedan koden som tar bort största värdet ur listan. Om det värdet finns två gånger, ta bort det ena. Redogör för (minst) 2 olika sätt det kan göras på.# Skapa en lista med 10 slumpade tal: my_list = [] for i in range(10): my_list.append(random.randint(0, 10)) # Ta bort största talet, alternativ 1: my_list.remove(max(my_list)) print('Listan med största elementet borttaget: ',my_list) # Ta bort största talet, alternativ 2: my_list.sort() # ELLER my_list = sorted(my_list) my_list.pop() print('Listan sorterad med största elementet borttaget: ',my_list)
-
Antag att
lista = ['a', 'b', 'c']
. Vad är blir resultatet avlista.append([1, 2])
ochlista.extend([1, 2])
?Prova! -
Skriv en funktion
extend(lista, x)
som gör samma sak somlista.extend(x)
gör utan att använda metodenextend
. Förutsätt attx
är en lista.def extend(lista, x): for z in x: lista.append(z)
-
Metoden
remove
tar bort första förekomsten av värdetx
på översta nivån. Skriv en funktionremove_all(lista, x)
som tar bort alla förekomster avx
på översta nivån i listanlista
.def remove_all(lista, x): while x in lista: lista.remove(x)
Anmärkning: Eftersom anropetlista.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 med list comprehension (listbyggare)
En vanlig situation är att man vill bygga upp en ny lista utifrån en gammallistaOld
.
I flera tidigare och exempel och övningar har vi sett mönstret
Detta mönster hade vi senast i lösningen till övning 3 ovan där vi också påpekade att den givna lösningen var ineffektiv för långa listor.
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 utom de med värdet x kan vi skriva:
remove_all(lista, x)
får vi alltså skriva
remove_all(lista, x)
, ty lista
får ny referens.
Metoden remove_all(lista, x)
måste returnera lista
)
Denna konstruktion kallas för "list comprehension" på engelska. Vi kommer använda termen listbyggare på svenska.
Fler exempel:
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 är likt listor men de är "immutable" 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 | 13 | Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment |
'x', 'a', 'y' | ('x', 'a', 'y') | Kan (ibland) skrivas utan parenteser. Se return-satsen från lösningen av andragradsekvationen, nätlektion 4 "Funktioner med flera returvärden"! |
sorted(t, reverse=True) | 13, 12, 11, 10 | Standardfunktioner går bra. Funktionen returnerar en tupel |
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ärt om). 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.
Nu följer de obligatoriska uppgifterna som skall redovisas, nämligen deluppgift 1a, 1b, 1c, 2 och 3
Notera att den pythonkod du redovisar skall följa kodningsreglerna
-
- Skriv en funktion
delete(my_list, number)
som fungerar som metodenremove
. Funktionen ska ta en lista och ett heltal som parametrar. Om heltalet finns i listan ska det tas bort. Om heltalet finns i listan flera gånger räcker det med att funktionen tar bort ett av dem.
Du får använda metoder i list-klassen men inte metodenremove
.
Funktionen ska inte returnera något värde.Testa din funktion
delete
så här:- Skapa en lista och fyll den med slumpade heltal i intervallet [0,10].
- Slumpa ett heltal i samma intervall och spara i en variabel.
- Anropa funktionen
delete
med din lista och heltalet. - Skriv ut listan så du ser att funktionen tagit bort talet, om det fanns i listan från början.
- Skriv koden som tömmer listan enligt följande.
Upprepa tills listan är tom:- slumpa ett tal i intervallet [0, 10]
- anropa funktionen
delete
med listan och det slumpade talet
- Här ser du ett (felaktigt) förslag till funktionen
delete
. Testa koden. Förklara i ord varför talet 7 fortfarande finns kvar imy_list
vid sista utskriften.def delete(the_list, number): new_list = [] for n in the_list: if n == number: continue new_list.append(n) the_list = new_list print('listan i delete', the_list) my_list = [2, 5, 1, 7, 12, 0, -5] print('my_list före delete', my_list) delete(my_list, 7) print('my_list efter delete', my_list) # har 7 tagits bort från listan?
Tips: se Föreläsning 4, rubriken "Skriva egna funktioner för listor, lista som parameter, returnera lista." på sidan 21.
- Skriv en funktion
- Skriv funktionen
smooth_a(x, n)
som tar emot en lista x med tal och ett icke-negativt heltal n. Funktionen ska skapa och returnera en ny lista r därri = (xi-n + xi-n-1 + xi-n-2 + ... + xi-1 + xi + xi+1 + ... + xi+n-1 + xi+n) / (2*n + 1)
dvs medelvärdet av xi och de 2n omkringliggande talen.Om n = 1 blir resultatet som i övningen ovan, åtminstone i de inre punkterna.
Notera hur funktionen hanterar punkterna som ligger nära kanterna hanteras, dvs när intervallet [i-n:i+n] är utanför listan: bredden (2n+1) på utjämningsoperatorn ska användas men i punkterna som får index mindre än 0 ska det första värdet användas och i punkter som får index efter sista elementet ska sista elementet 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.
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))ska ge följande utskriftersmooth_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]Försök använda list-operatorer, skivningar och listmetoder så mycket som möjligt! Det går att klara sig helt utanif
-satser, försök detta.Notera att funktionen
smooth_a(x, n)
inte får ändra elementens värden i listanx
. Du kan testa detta genom att skriva ut listanx
efter varje anrop av funktionensmooth_a(x, n)
. -
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: Koden
print('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]Du måste använda "list comprehension" för att lösa uppgiften. Tips för avrundningen: Använd pythons inbyggda funktionround
.Frivillig uppgift
- Skriv funktionen
smooth_b(x, n)
som, precis somsmooth_a
, tar emot en lista x med tal och ett icke-negativt heltal n. Funktionen ska, precis somsmooth_a
skapa och returnera en ny lista r därri = (xi-n + xi-n-1 + xi-n-2 + ... + xi-1 + xi + xi+1 + ... + xi+n-1 + xi+n) / (2*n + 1)
dvs medelvärdet av xi och de 2n omkringliggande talen.Om n = 1 blir resultatet som i övningen ovan, åtminstone i de inre punkterna.
Skillnaden mellan de båda funktionerna är hur punkterna som ligger nära kanterna hanteras, dvs när intervallet [i-n:i+n] är utanför listan. Följande strategi skall implementeras försmooth_b(x, n)
:-
smooth_b(x, n)
: Bara punkter som finns i listan ska användas så medelvärdet bildas då över färre punkter.
Exempel: Koden
x = [1, 2, 6, 4, 5, 0, 1, 2] print('smooth_b(x, 1): ', smooth_b(x, 1)) print('smooth_b(x, 2): ', smooth_b(x, 2))ska ge följande utskriftersmooth_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, skivningar och listmetoder så mycket som möjligt! Det går att klara sig helt utanif
-satser, försök detta.Notera att funktionen
smooth_b(x, n)
inte får ändra elementens värden i listanx
. Du kan testa detta genom att skriva ut listanx
efter varje anrop av funktionernasmooth_b(x, n)
. -
Fråga
Hur många timmar har du arbetat med denna lektion?
Gå till nästa lektion eller gå tillbaka