Förberedelsematerial 3.1
Innehåll
Inför seminariet ska du gå igenom materialet nedan, vilket bör ta ca en timme, samt genomföra det tillhörande quizet som testar dina kunskaper på materialet. Quizet finns endast till för att du ska kunna skatta dina egna kunskaper inför seminariet, och är inget som du bedöms på eller som ens sparas.
Dictionary
Börja med att titta på videoklippet nedan. Om du tittat på tidigare videos tillhörande kursen så känner du igen Jody, som var examinator och kursledare fram till och med 2022.
Datatypen dictionary, dict
, är alltså en mängd av par av objekt så att vi med hjälp av det ena objektet kan få fram det objektet. I videon visade Jody t.ex. hur vi kan koppla ihop värden med en kort beskrivning av vad värdet representerar. Vi såg exemplet
>>> person1 = {'name': 'Guido', 'phonenumber': '0711-123456'}
där alltså person1
är en variabel som representerar en person och vi har ett dictionary som kopplar ihop olika egenskaper hos personen med egenskapens faktiska värde. Vi kan alltså säga t.ex. att personens namn är 'Guido'
och deras telefonnummer är '0711-123456'
.
Vi kunde sedan slå upp dessa värden med hjälp av subskriptnotationen, dvs. med hakparenteser. Vi kan alltså skriva person1['name']
för att få ut 'Guido'
och person1['phonenumber']
för att få ut '0711-123456'
. Vi kallade 'name'
och 'phonenumber'
för nycklar och 'Guido'
och '0711-123456'
för värden. Men vad händer om vi försöker slå upp en nyckel som inte finns?
>>> person1['name']
'Guido'
>>> person1['phonenumber']
'0711-123456'
>>> person1['hometown']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'hometown'
Ett sätt att undvika det här felet är att använda metoden dict.get
istället för subskriptnotationen när vi vill slå upp ett värde:
>>> person1.get('address')
>>> person1.get('name')
'Guido'
Här fick vi inget fel när vi försökte slå upp nyckeln 'address'
, vi fick helt enkelt inget alls (dvs. metoden dict.get
returnerar None
om nyckeln inte existerar).
När vi pratar om storleken av en dict
så pratar vi om antalet nyckel-värde-par. Det innebär att storleken av person1
är 2:
>>> len(person1)
2
Namnet “dictionary”
Namnet dictionary kommer av att den här datatypen fungerar på samma sätt som en ordbok där vi genom att slå upp ett visst ord får veta vad ordet betyder och hur det används. Vi kan alltså i det mest typiska fallet använda en dict
i Python just som en ordbok. Vi kan t.ex. tänka oss att Svenska Akademiens Ordbok skulle se ut som följande, ifall kognition var det enda uppslagsordet.
>>> saol = {'KOGNITION':"Ordformer\n(förr äv. skrivet cogn-)\nEtymologi\n[jfr t. kognition (i bet. 1, 2), eng. cognition (i bet. 1, 2), fr. cognition (i bet. 1); av lat. cognitio (gen. -ōnis), inlärande, (rättslig) undersökning, vbalsbst. till cognoscere (se KOGNOSCERA)]\n(†)\n1) kunskap, kännedom. LReg. 431 (1662). Innan en får en perfect cognition af alla böcker .. behöfver han många åhr Biblioteket upwakta. Rudbeck Bref 176 (1679). ÄSvBiogr. 5: 144 (c. 1722). Ekbohrn (1904).\n2) efterforskning, undersökning; särsk.: rättslig undersökning. RP 12: 402 (1648). Ährender, som til General-Krigz-Rättens cognition ock domb höra. 2RARP 2: 236 (1723). Ekbohrn (1904)."}
>>> print(saol['KOGNITION'])
Ordformer
(förr äv. skrivet cogn-)
Etymologi
[jfr t. kognition (i bet. 1, 2), eng. cognition (i bet. 1, 2), fr. cognition (i bet. 1); av lat. cognitio (gen. -ōnis), inlärande, (rättslig) undersökning, vbalsbst. till cognoscere (se KOGNOSCERA)]
(†)
1) kunskap, kännedom. LReg. 431 (1662). Innan en får en perfect cognition af alla böcker .. behöfver han många åhr Biblioteket upwakta. Rudbeck Bref 176 (1679). ÄSvBiogr. 5: 144 (c. 1722). Ekbohrn (1904).
2) efterforskning, undersökning; särsk.: rättslig undersökning. RP 12: 402 (1648). Ährender, som til General-Krigz-Rättens cognition ock domb höra. 2RARP 2: 236 (1723). Ekbohrn (1904).
En viktig insikt är dock att vi kan använda det här sättet att organisera data för så mycket mer än bara ord och deras definitioner. Alla icke-muterbara datatyper kan användas som nycklar, medan alla datatyper kan användas som värden. Vi kan t.ex. tänka oss ett dictionary som hjälper oss att översätta siffror till hur de skall utläsas, dvs. {1: 'one', 2: 'two', 3: 'three', ...}
. En annan viktig skillnad mellan nycklar och värden är att nycklar måste vara unika, men värden kan förekomma hur många gånger som helst.
I just Python heter den här datatypen dictionary och vi säger oftast det även på svenska. Detta är ett förhållandevis vanligt namn på den här typen av datastruktur. Ett mer generellt namn är dock associationslista, eftersom vi associerar en viss nyckel med ett visst värde. Man hör också associationstabell som tydliggör att det är en typ av tabell med två sammanhörande objekt.
Andra populära namn är mappning eller bara tabell. På engelska ser vi namn som associative array, associative table, hash table, hash map och tree map. I de sista fallen refererar hash och tree till två olika sätt som datatypen kan fungera under ytan. Vi ska inte gå närmare in på det nu eftersom sättet vi använder datatypen är detsamma oavsett vilket, men för den som är nyfiken kan vi säga så mycket som att Pythons dictionary använder hash-tekniken.
Liknelse: Dictionary som en förenklad funktion
Vi kan också tänka på ett dictionary som en förenklad typ av funktion där vi har specificerat alla godkända argument och motsvarande returvärde i förväg. T.ex. skulle dictionaryt person1 = {'name': 'Guido', 'phonenumber': '0711-123456'}
kunna representeras av en funktion på följande sätt:
|
|
Vi kan nu använda person1_fun
på nästan exakt samma sätt som dictionaryt person1
, men eftersom det är en funktion, och nyckeln nu är ett argument, så får vi ersätta subskriptnotationen (person1['name']
) med ett funktionsanrop (person1('name')
).
>>> person1_fun('name')
'Guido'
>>> person1_fun('phonenumber')
'0711-123456'
>>> person1_fun('hometown')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in person1_fun
KeyError: 'hometown'
Det var ju dock mycket mer jobb att skriva person1_fun
än motsvarande dictionary. En annan fördel med ett dictionary är det är en muterbar datatyp. Dvs. vi kan lägga till nya nyckel-värde-par, eller ändra på värdet tillhörande en viss nyckel, genom att bara göra en tilldelning till en uppslagning:
person1['hometown'] = "The Hague"
person1['phonenumber'] = "Not listed"
Skulle vi vilja göra motsvarande ändringar i person1_fun
så skulle vi behöva definiera hela funktionen en gång till.
Vyer och iteration
Här kommer ytterligare en video som visar hur vi kan loopa över ett dictionary med hjälp av olika vyer.
Precis som mängder, set
, så är dict
oordnad. Vi kan alltså inte lita på att paren kommer i någon viss ordning eller prata om “index”. Vi kan däremot iterera över en dict
genom att skapa olika vyer. Det finns i första hand tre vyer vi använder oss av och som vi kommer åt med hjälp av metoderna dict.keys
, dict.values
och dict.items
.
>>> person1.keys()
dict_keys(['name', 'phonenumber', 'hometown'])
Den vanligaste vyn är dict_keys
som returneras t.ex. av metodanropet person1.keys()
. Som vi ser i exemplet ovan ser det ut ungefär som en lista och elementen kommer ligga i den ordning som nycklarna ursprungligen lades till. Notera att vi ju uppdaterade person1['phonenumber']
efter att vi lade till person1['hometown']
men 'phonenumber'
kommer fortfarande före 'hometown'
i person1.keys()
. Den här ordningen är dock inte tillförlitlig i alla lägen, och vi kan ändra på den genom att istället för att bara uppdatera person1['phonenumber']
, ta bort och sedan lägga till person1['phonenumber']
igen:
>>> del person1['phonenumber']
>>> person1.keys()
dict_keys(['name', 'hometown'])
>>> person1['phonenumber'] = "Not listed"
>>> person1.keys()
dict_keys(['name', 'hometown', 'phonenumber'])
Alltså, vi måste alltid komma ihåg att ordningen på elementen i vyer inte är tillförlitlig. Pythontolken hjälper oss till viss del genom att inte tillåta indexering av vyer heller:
>>> person1.keys()[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'dict_keys' object is not subscriptable
Det enda som en vy garanterar är att varje element förekommer exakt en gång. Det räcker dock för att vi ska kunna loopa över t.ex. nycklarna:
>>> for key in person1.keys():
... print(key)
...
name
hometown
phonenumber
Eller över värdena:
>>> for value in person1.values():
... print(value)
...
Guido
The Hague
Not listed
Notera att även den här vyn är sorterade efter den ordning som nycklarna ursprungligen lades till:
>>> person1['hometown'] = 'Belmont'
>>> person1.values()
dict_values(['Guido', 'Belmont', 'Not listed'])
Vi kan också använda t.ex. in
-operatorn på en vy för att undersöka om en viss nyckel eller ett visst värde finns i en dict:
>>> 'Guido' in person1.values()
True
>>> 'address' in person1.keys()
False
Här är ytterligare ett sätt att undvika det där KeyError
-felet som vi fick när vi försökte slå upp en nyckel som inte fanns, vi kan helt enkelt kontrollera att nyckeln finns i person1
innan vi försöker slå upp den.
Innan vi går vidare till den tredje vyn ska vi illustrera vad som händer om vi gör en operation på en dict
som kräver en vy utan att explicit säga vilken vy:
>>> for element in person1:
... print(element)
...
name
hometown
phonenumber
>>> 'Guido' in person1
False
Här kan vi se att pythontolken automatiskt kommer använda dict_keys
om inget annat anges. Detta är anledningen till att vi kallar dict_keys
för den “vanligaste” vyn, det är den man får by default om inget annat anges.
Det sista vi ser är dock en mycket vanlig fallgrop som följer av att dict_keys
-vyn är standardvy. Vi vet ju att 'Guido'
faktiskt finns i person1
, men eftersom vi inte specificerade att vi skulle leta ibland värdena så sökte python efter 'Guido'
bland nycklarna, och där finns ju ingen 'Guido'
. Alltså blev 'Guido' in person1
faktiskt False
, tvärt emot vad vi kanske hade väntat oss. Även vana pythonprogrammerare tänker fel här ibland och vi är många som kanske hade önskat att det fungerade på ett annat sätt. Min starka rekommendation är att ni alltid använder en explicit vy, tills ni känner er trygga med hur dict
fungerar.
Den tredje vyn, dict.items
, är kanske den mest användbara för just iteration. Oftast vill vi ju ha tillgång till både nyckeln och det tillhörande värdet. Har vi en nyckel kan vi alltid hämta det motsvarande värdet, men det är klumpigt och (även om det inte är det viktigaste vid det här laget) djupt ineffektivt. Istället kan vi använda dict.items
och få tillgång just till både nyckel och värde på samma gång.
>>> person1.items()
dict_items([('name', 'Guido'), ('hometown', 'Belmont'), ('phonenumber', 'Not listed')])
Vi ser att vi får en vy med tupler där varje tupel har två element, en nyckel och dess värde. De som korrekt löste swap
-uppgiften från muterbarhetskapitlet kommer kanske ihåg konceptet tuple assignment, eller tuple unpacking, som konceptet kallas mer generellt. Dvs. att vi kunde göra på följande sätt för att tilldela flera värden samtidigt:
>>> a, b, c = (1, 2, 3)
>>> print(a)
1
>>> print(b)
2
>>> print(c)
3
Att vi repeterar det här nu är för att en loop över dict.items
utgör den kanske absolut vanligaste tillämpningen av sådan tuple unpacking:
>>> for key, value in person1.items():
... print(f"Nyckeln är {key} och värdet är {value}.")
...
Nyckeln är name och värdet är Guido.
Nyckeln är hometown och värdet är Belmont.
Nyckeln är phonenumber och värdet är Not listed.
Notera att vi här har två loopvariabler, key
och value
. Ett vanligt missförstånd är att pythontolken automatiskt kommer loopa över nyckel-värde-par om vi har två loopvariabler, men så är inte fallet:
>>> for key, value in person1:
... print(f"Nyckeln är {key} och värdet är {value}.")
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
Det är just dict.items
som avgör att vi har två värden och kan göra tuple unpacking, men vi måste inte. Vi kan se det genom att vi också kan loopa över dict.items
med bara en loopvariabel:
>>> for pair in person1.items():
... print(f"Paret är {pair}.")
...
Paret är ('name', 'Guido').
Paret är ('hometown', 'Belmont').
Paret är ('phonenumber', 'Not listed').
För att se att tuple unpacking är en generell egenskap hos tupler och inte något som har med for
-loopen eller med dict.items
att göra så kan vi köra följande:
>>> for element in person1.items():
... print(f"Paret är {element}.")
... first, second = element
... print(f"Nyckeln är {first} och värdet är {second}.")
...
Paret är ('name', 'Guido').
Nyckeln är name och värdet är Guido.
Paret är ('hometown', 'Belmont').
Nyckeln är hometown och värdet är Belmont.
Paret är ('phonenumber', 'Not listed').
Nyckeln är phonenumber och värdet är Not listed.
Nästlade datastrukter
Hittills i kursen har vi i första hand hanterat ganska så enkla datastrukturer. Dvs. listor och tupler har bara innehållit strängar och tal. I avsnittet om mängdlära såg vi dock att en mängd kunde innehålla andra mängder som element. Vi pratade t.ex. om potensmängden, som var mängden av alla delmängder till en mängd:
$$ M = \{a, b\} \Leftrightarrow 2^M = \{\emptyset, \{1\}, \{2\},\{1, 2\}\} $$
Samma princip gäller faktiskt för alla pythons samlingstyper. Samlingar är de datatyper som innehåller andra objekt som element. De samlingar vi sett hittills är list
, tuple
, set
och nu senast dict
. Vi betraktar normalt sett inte str
som en samling eftersom “elementen” själva alltid är strängar och inte existerar oberoende av den ursprungliga strängen förrän vi faktiskt använder index eller slice för att ta ut en sådan delsträng.
Precis som en mängd kan innehålla en annan mängd så kan en lista innehålla en annan lista, eller en tupel, eller en mängd. Vi kallar en datastruktur som i någon utsträckning består av samlingar i samlingar för en nästlad datastruktur:
>>> list_of_lists = [[1, 2], [3, 4]]
>>> print(list_of_lists)
[[1, 2], [3, 4]]
Det behöver inte vara listor i listor eller ens samma datatyper, och inte alla element måste vara samlingar:
>>> list_of_many_things = [[1, 2], (3, '4'), 5, 6.0]
>>> print(list_of_many_things)
[[1, 2], (3, '4'), 5, 6.0]
Det finns inte heller någon begränsning på hur djupt vi kan nästla samlingar och alla nivåer av nästling kan innehålla både “atomära” element (element är samlingar) och ytterligare nästlade samlingar:
>>> deeply_nested_empty_list = [[[[[[[[[[]]]]]]]]]]
>>> print(deeply_nested_empty_list)
[[[[[[[[[[]]]]]]]]]]
>>> deeply_nested_structure = [1, [{2: 'two'}, 3], [([4], {5})], 6]
>>> print(deeply_nested_structure)
[1, [{2: 'two'}, 3], [([4], {5})], 6]
deeply_nested_structure
representerad som ett träd.Vi kan också beskriva en struktur som deeply_nested_structure
som ett träd där vi ritar ut element på samma nivå av nästling på samma rad. I bilden till höger kan vi t.ex. se att {2: 'two'}
, 3
och [[4], {5}]
alla är på samma nivå av nästling, eller samma djup i strukturen.
Precis som med kardinaliteten hos mängder så är längden av en samling bara antalet element som som finns på den första nivån. Vi kan också se vilka element detta är genom att loopa över samlingen och skriva ut varje element:
>>> len(deeply_nested_structure)
4
>>> for elem in deeply_nested_structure:
... print(elem)
...
1
[{2: 'two'}, 3]
[[[4], {5}]]
6
Vilka samlingar som kan nästlas i varandra beror enbart på vilken typ av data en viss samling kan innehålla. Vi vet från vårt arbete med mängder att vi inte kunde ha vanliga mängder i mängder i Python, vi var tvugna att använda frozenset
. Det beror på att mängder inte kan innehålla muterbara värden, och detsamma gäller för nycklarna i dict
(värdena får dock vara muterbara). Det innebär att vi får problem om vi försöker stoppa listor, mängder eller dictionaries i mängder eller som nycklar i dictionaries. Bortsett från det går det dock bra hur som helst.
Fördefinierade nästlade datastrukturer
Nu vet vi att nästlade datastrukturer kan sättas ihop på nästan vilket sätt som helst. I väldigt många fall vet vi dock i förväg hur strukturen kommer se ut. Hur många nivåer av nästling som finns, vilka datatyper som ligger nästlade i varandra, och vad olika delar av strukturen representerar.
Vi kommer här att kalla dessa nästlade datastrukturer för fördefinierade nästlade datastrukturer. Vi kommer att prata mer om godtyckligt nästlade strukturer, och hur de kan hanteras med hjälp av rekursion, nästa vecka.
Fördelen med en fördefinierad nästlad datastruktur är att vi förhållandevis enkelt kan använda oss av loopar för att bearbeta strukturen. Notera förhållandevis, för detta är inte nödvändigtvis enkelt, inte ens för vana programmerare.
Det absolut vanligaste är att fördefinierade nästlade datastrukturer består av listor och dictionaries som är nästlade i varandra på något sätt. Det populära textbaserade dataformatet JSON kan sägas bestå just av nästlade listor och dictionaries. Även om JSON tekniskt sett kan betraktas som en godtyckligt nästlad struktur, så används det oftast på ett mer fördefinierat sätt.
JSON
Vi börjar med ett kanske lite överväldigande exempel, bara för att se ungefär hur det kan se ut i verkligheten och hur man kan tänka kring det. Föreställ dig att vi har fått data från en webbtjänst som representerar användare och deras inställningar. Den datastrukturen skulle kunna se ut så här:
data = {"users": [{"id": 1, "name": "Alice", "roles": ["admin", "editor"], "profile": {"age": 30, "email": "alice@example.com", "preferences": {"theme": "dark", "notifications": True}}}, {"id": 2, "name": "Bob", "roles": ["viewer"], "profile": {"age": 25, "email": "bob@example.com", "preferences": {"theme": "light", "notifications": False}}}], "settings": {"site_name": "MyApp", "maintenance_mode": False, "features": ["chat", "analytics", "payments"]}}
Om din reaktion när du ser den här datastrukturen är ungefär “AAAAAAAAAAAAAAAHH!”, eller i bästa fall, “Vad i hela friden är det här för något?”, så är du inte ensam. Det här är som sagt en datastruktur som skulle kunna representera data från en webbtjänst med användare och deras inställningar. Den är dock inte enkel att läsa eller förstå. Vi kan dock låta Python formatera om den så att den blir åtminstone lite lättare att läsa. Vi kan göra det med hjälp av Pythons inbyggda json
-modul, som har en funktion som heter json.dumps
som kan göra detta åt oss:
|
|
Nu är det lite tydligare att den här datastrukturen är fördefinierad i den meningen att den följer ett strikt mönster. Strukturen påminner lite om ett filträd och det är lättare att se vilka delar som hör ihop med vad.
Vi ser vilka nycklar som finns i varje dictionary på varje nivå, och vilken typ av data som finns i listorna. Även om den här utskriften är mer överskådlig så är det jätteviktigt att inse att ingen förväntar sig att någon programmerare ska titta på en sån här datastruktur och utan minsta eftertanke eller experimenterande kunna skriva en rad kod som besvarar frågan “Vilket färgtema använder den första användaren?”. Inte ens erfarna programmerare kan det. Om vi bara läser uppifrån och ner så hittar vi svaret (på rad 15). Så enkelt är det ju dock inte i python.
Vad vi istället måste lära oss är hur vi systematiskt kan navigera i den här datastrukturen för att hitta det vi letar efter. Om vi börjar med att läsa frågan “Vilket färgtema använder den första användaren?” så kan vi bryta ner den i mindre delar. Vi börjar med att identifiera den första användaren, och sedan tittar vi på vad som krävs för att hitta den information vi vill ha om just den användaren.
Vi tittar på den översta, eller yttersta, nivån av nästling. Det är ett dictionary med två nycklar, 'users'
och 'settings'
. Eftersom vi vill ha svara på en fråga som rör en av användarna så börjar vi med att plocka ut värdet tillhörande nyckeln 'users'
:
>>> print(json.dumps(data["users"], indent=4))
[
{
"id": 1,
"name": "Alice",
"roles": [
"admin",
"editor"
],
"profile": {
"age": 30,
"email": "alice@example.com",
"preferences": {
"theme": "dark",
"notifications": true
}
}
},
{
"id": 2,
"name": "Bob",
"roles": [
"viewer"
],
"profile": {
"age": 25,
"email": "bob@example.com",
"preferences": {
"theme": "light",
"notifications": false
}
}
}
]
Här har vi plockat ut data["users"]
. Vi använde subskriptnotationen för att göra en uppslagning i en dict som returnerar värdet tillhörande nyckeln "users"
i dictionaryt data
. Det värdet var en lista med två element, där varje element är ett dictionary som representerar en användare. Vi kan nu utgå från den här listan. Vi kan också tänka på det som att vi har substituerat data
med data["users"]
i vår ursprungliga fråga “Vilket färgtema använder den första användaren?”.
I nästa steg väljer vi det första elementet i den här listan eftersom vi ville veta vilket färgtema just den första användaren använder vilket ger oss det dictionary som representerar den första användaren, Alice:
>>> print(json.dumps(data["users"][0], indent=4))
{
"id": 1,
"name": "Alice",
"roles": [
"admin",
"editor"
],
"profile": {
"age": 30,
"email": "alice@example.com",
"preferences": {
"theme": "dark",
"notifications": true
}
}
}
Vi får dock fortsätta några steg till “ner i strukturen”. Nästa steg är att slå upp nyckeln "profile"
i dictionaryt som representerar Alice, vilket ger oss ytterligare ett dictionary:
>>> print(json.dumps(data["users"][0]["profile"], indent=4))
{
"age": 30,
"email": "alice@example.com",
"preferences": {
"theme": "dark",
"notifications": true
}
}
Nu är vi snart framme vid målet. Nästa steg är att slå upp nyckeln "preferences"
i det här dictionaryt, vilket ger oss det innersta dictionaryt vi behöver:
>>> print(json.dumps(data["users"][0]["profile"]["preferences"], indent=4))
{
"theme": "dark",
"notifications": true
}
Härifrån är det bara ett steg kvar. Vi slår upp nyckeln "theme"
i det här dictionaryt och får ut värdet 'dark'
. Vi behöver inte längre bry oss om resten av datastrukturen och kan svara på vår ursprungliga fråga utan json.dumps
:
>>> data["users"][0]["profile"]["preferences"]["theme"]
'dark'
Vi hade en variabel data
som innehöll ett dictionary med två nycklar, 'users'
och 'settings'
. Värdet tillhörande 'users'
var en lista med två element, där varje element var ett dictionary som representerade en användare. Värdet tillhörande 'settings'
var ett dictionary med inställningar för hela webbtjänsten.
Vad vi gjorde i det här exemplet var att vi navigerade i den här datastrukturen genom att steg för steg plocka ut det vi behövde. Vi kunde göra det eftersom datastrukturen trots allt inte var helt godtyckligt nästlad, utan följde ett visst mönster och vi kunde läsa oss till hur den var uppbyggd. Vi hade också turen att den fråga vi ville besvara var relativt enkel och datastrukturen tillräckligt liten för att få plats på en skärmsida.
Enklare struktur, svårare fråga
En enklare struktur är en lista av dictionaries där varje dictionary representerar en person med namn och telefonnummer enligt det mönster vi såg i avsnittet om dictionaries:
|
|
Säg att vi vill ta reda på telefonnumret till personen med namnet 'Linus'
. Vi kan inte bara göra en uppslagning eftersom vi inte vet i förväg på vilken position i listan people
som personen med namnet 'Linus'
finns. Vi måste alltså loopa över listan och titta på varje element, som ju är ett dictionary, och se om nyckeln 'name'
har värdet 'Linus'
. Om den har det så kan vi plocka ut värdet tillhörande nyckeln 'phonenumber'
i samma dictionary.
|
|
>>> print(get_number('Linus', people))
0700-654321
>>> print(get_number('Margaret', people))
None
En annan sak vi skulle kunna besvara är “Ge mig en lista med telefonnummer till alla personer vars namn börjar på bokstaven ‘G’.” Vi kan göra det genom att skapa en tom lista och sedan loopa över people
och lägga till telefonnumret i den nya listan om namnet börjar på 'G'
:
|
|
>>> print(get_numbers_of_names_starting_with('A', people))
['0733-987654']
>>> print(get_numbers_of_names_starting_with('G', people))
['0711-123456', '0722-456789']
Nästlade loopar för nästlade datastrukturer
I många situationer behöver vi använda oss av nästlade loopar för att bearbeta nästlade datastrukturer. Vi kan t.ex. tänka oss att vi har en lista av dictionaries där varje dictionary representerar en person med namn och en lista med kurser de läst:
|
|
Säg att vi vill ta reda på alla kurser som någon av studenterna läst. Vi kan göra det genom att skapa en tom mängd och sedan loopa över students
och för varje student loopa över dennes lista med kurser och lägga till varje kurs i mängden:
|
|
>>> print(get_all_courses(students))
{'Math', 'Biology', 'History', 'Chemistry', 'Physics'}
I många fall kan vi komma runt behovet av nästlade loopar genom att använda oss av andra funktioner och metoder. T.ex. skulle vi kunna använda oss av mängdoperationer för att lösa samma problem som ovan:
|
|
>>> print(get_all_courses_setop(students))
{'History', 'Biology', 'Chemistry', 'Physics', 'Math'}
En annan vanlig situation är att vi vill ta reda på vilka studenter som läst vissa kurser. Vi kan t.ex. tänka oss att vi vill ta reda på vilka studenter som läst antingen 'Math'
eller 'Physics'
.
|
|
>>> print(get_students_in_courses(students, {'Math', 'Physics'}))
['Alice', 'Chuck', 'Diana']
Åter igen kan vi förenkla funktionen något genom att använda oss av mängdoperationer:
|
|
>>> print(get_students_in_courses_setop(students, {'Math', 'Physics'}))
['Alice', 'Chuck', 'Diana']
Quiz
Testa din förståelse av materialet med tillhörande quiz.
Sidansvarig: Johan Falkenjack
Senast uppdaterad: 2025-09-28