Lijsten — veel waarden onder één naam
Zoveel waarden als je wilt onder één naam bewaren, erin kijken met een index en een slice, de methoden die een lijst veranderen, en de valkuil van twee namen die uiteindelijk aan één lijst vastzitten.
Hoe wil je leren?
Het probleem dat we oplossen
Een winkel moet de prijzen van drie producten bijhouden. Met alles wat we tot nu toe hebben geleerd is er maar één manier om dat te doen:
price_1 = 15
price_2 = 60
price_3 = 850
print(price_1 + price_2 + price_3)925Nu de vraag: wat als je veertig producten hebt? Veertig variabelen. En een regel rekenwerk met veertig stukken erin. En wanneer de winkel iets nieuws begint te verkopen, moet je in de code gaan zitten en nog een variabele verzinnen.
Het probleem zit niet alleen in het getyp, maar in de vorm. Voor Python zijn price_1, price_2 en price_3 drie totaal losse namen — net zo los als name en age. Jij weet dat het drie exemplaren van hetzelfde ding zijn. Maar we hebben nog geen manier om "veel van hetzelfde ding" in code te schrijven.
Een lijst is die manier. Eén naam, zoveel waarden als je eronder wilt, op volgorde.
Aan het einde van dit hoofdstuk kun je
- Een lijst maken en erin kijken met een index en een slice
- Een lijst veranderen met
append,insert,removeenpop - Uitleggen waarom een lijst veranderd kan worden en een stuk tekst niet
- Herkennen en vermijden dat
b = atwee namen aan één lijst koppelt - Een
IndexErroren eenValueErrorlezen en zeggen wat er misging
Voorkennis: for-lussen en range.
Een lijst maken
Vierkante haken, met de waarden gescheiden door komma's:
prices = [15, 60, 850]
print(prices)
print(len(prices))[15, 60, 850]
3len() werkt hier precies zoals bij tekst — drie waarden, dus 3.
Afprinten laat de hele lijst zien, haken inbegrepen. Dat is niet voor de gebruiker om te lezen, dat is voor jou om te begrijpen: [15, 60, 850] vertelt je dat dit een lijst is, niet 925.
Erin kijken
Indexeren werkt precies zoals bij strings — vanaf nul tellen, negatief vanaf de achterkant:
prices = [15, 60, 850]
print(prices[0])
print(prices[-1])
print(prices[1:])15
850
[60, 850]Alle drie de regels zijn de regels van hoofdstuk zes, ongewijzigd. Eén verschil is het vermelden waard: prices[0] geeft een getal terug, terwijl prices[1:] een lijst teruggeeft. Een slice geeft altijd hetzelfde soort ding terug als waaruit hij is gesneden, nooit een enkel stukje.
En hier is het echte verschil: een lijst kan veranderd worden
In hoofdstuk zes zagen we dat een teken binnen tekst niet verwisseld kan worden:
name = "rafi"
name[0] = "R"TypeError: 'str' object does not support item assignmentDe identieke regel werkt op een lijst:
prices = [15, 60, 850]
prices[0] = 20
print(prices)[20, 60, 850]Er is geen nieuwe lijst gemaakt — deze lijst is veranderd. Dat is de belangrijkste eigenschap van een lijst, en de valkuil aan het einde van dit hoofdstuk volgt daar rechtstreeks uit.
De woorden daarvoor: tekst is immutable, een lijst is mutable.
Toevoegen en weghalen
stock = ["pen", "notebook"]
stock.append("bag")
stock.insert(1, "eraser")
print(stock)
stock.remove("pen")
print(stock)
last = stock.pop()
print(last)
print(stock)['pen', 'eraser', 'notebook', 'bag']
['eraser', 'notebook', 'bag']
bag
['eraser', 'notebook']Vier methoden, vier verschillende klussen:
append(x)— voegt toe aan het eindeinsert(i, x)— zet het op positiei, en schuift alles daarna naar rechtsremove(x)— vindt het eerste item met die waarde en gooit het wegpop()— gooit het laatste item weg en geeft het terug
Tussen die laatste twee zit de meeste verwarring: remove wil weten wat er weg moet, pop wil weten vanaf waar. En pop geeft je het ding dat het verwijderde; remove niet.
Deze methoden geven de lijst niet terug. Bij tekst moest je het resultaat vanname.upper()bewaren, omdat het nieuwe tekst bouwde en teruggaf. Lijsten zijn andersom:appendverandert de lijst zelf en geeftNoneterug.
Wat dit een klassieke fout maakt:
stock = ["pen", "notebook"]
stock = stock.append("bag")
print(stock)NoneDe lijst is weg. Schrijf stock.append("bag"), niet stock = stock.append("bag").
Zoeken en doorlopen
stock = ["pen", "notebook", "bag"]
print("pen" in stock)
print("chair" in stock)
for item in stock:
print(item.upper())True
False
PEN
NOTEBOOK
BAGin zocht in tekst, en hij zoekt ook in een lijst — hetzelfde woord doet dezelfde klus.
En for item in stock loopt de waarden één voor één door. Let op dat er geen range is en geen indexrekenwerk, precies zoals toen we in hoofdstuk elf de tekens van een woord doorliepen.
Wanneer je de positie en de waarde nodig hebt, is er enumerate:
stock = ["pen", "notebook"]
for position, item in enumerate(stock, start=1):
print(position, item)1 pen
2 notebookZonder start=1 begint de telling bij nul. Een lijst die aan een mens wordt getoond, begint meestal bij één.
De valkuil die iedereen vangt
In hoofdstuk vier zagen we dit:
a = 3
b = a
b = b + 1
print(a, b)3 4b kreeg een waarde en lette niet op a. Doe nu hetzelfde met een lijst:
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)[1, 2, 3, 4]
[1, 2, 3, 4]b is veranderd, en a veranderde ook.
De reden weerspreekt de eerdere regel niet, het is de eerdere regel. Op de regel b = a kreeg b precies wat a had — en wat a had was waar de lijst staat, niet een kopie van de lijst. Dus wijzen beide namen nu naar dezelfde lijst. b.append(4) veranderde die ene lijst, en beide namen laten dezelfde verandering zien.
Dit zie je nooit bij getallen, want b = b + 1 verandert het oude getal niet — het maakt een nieuw getal en zet dat in b. Getallen zijn juist veilig omdat ze niet veranderd kunnen worden. Lijsten wel, en dus zijn ze dat niet.
Wil je een aparte kopie, dan moet je erom vragen:
a = [1, 2, 3]
b = a.copy()
b.append(4)
print(a)
print(b)[1, 2, 3]
[1, 2, 3, 4]Wanneer oppassen. Elke keer dat je een lijst onder een tweede naam zet, of een lijst doorgeeft aan een functie (hoofdstuk negentien), vraag je jezelf één ding af: bedoel ik deze te veranderen, of wil ik een kopie? De mensen die die vraag nooit stellen, besteden dagen aan het jagen op bugs die lezen als "maar ik heb hier niets veranderd".
Een compleet voorbeeld
shopping.py:
# A shopping list, built up and then summarised
items = []
items.append("pen")
items.append("notebook")
items.append("bag")
items.insert(0, "eraser")
print("List :", items)
print("Count :", len(items))
print("First :", items[0])
print("Last :", items[-1])
print("Has bag :", "bag" in items)
items.remove("pen")
dropped = items.pop()
print()
print("After removing 'pen' and popping the last one:")
print("List :", items)
print("Dropped :", dropped)
print("Count :", len(items))
print()
print("Numbered:")
for position, item in enumerate(items, start=1):
print(f" {position}. {item}")List : ['eraser', 'pen', 'notebook', 'bag']
Count : 4
First : eraser
Last : bag
Has bag : True
After removing 'pen' and popping the last one:
List : ['eraser', 'notebook']
Dropped : bag
Count : 2
Numbered:
1. eraser
2. notebookDrie dingen die het vermelden waard zijn.
Het begint met een lege lijst. De regel items = [] doet dezelfde klus als een accumulator buiten een lus in hoofdstuk tien — waar total = 0 stond, staat nu []. Je hebt ergens nodig om in te verzamelen, voordat je begint met verzamelen.
insert(0, "eraser") zette het vooraan, dus eraser is eerste in de afgedrukte lijst. De volgorde waarin dingen toegevoegd zijn, is niet de volgorde waarin ze uiteindelijk terechtkomen.
pop() gaf terug wat het verwijderde, dus dropped bevat bag. remove("pen") gaf niets terug, dus de verwijderde pen is gewoon weg.
Wanneer het misgaat
IndexError: list index out of range Een index voorbij het aantal waarden in de lijst. In een lijst van drie zijn de geldige indexen 0, 1, 2 — er is geen prices[3]. Voor de laatste waarde is prices[-1] het veiligste wat je kunt schrijven, want je hoeft de lengte niet te weten.
ValueError: list.remove(x): x not in list De waarde die je wilde verwijderen, staat er niet. Controleer eerst met if x in stock:, of gebruik try uit hoofdstuk vierentwintig.
AttributeError: 'NoneType' object has no attribute 'append' Een eerdere regel zei stock = stock.append(...), dus stock is nu None. Bewaar het resultaat niet — stock.append(...) op zichzelf is de hele handeling.
Ik veranderde één lijst en een andere veranderde ook Twee namen zitten aan één lijst vast. Schrijf b = a.copy() in plaats van b = a.
Ik verwijder in een lus en dingen worden overgeslagen
numbers = [1, 2, 2, 3, 4]
for n in numbers:
if n % 2 == 0:
numbers.remove(n)
print(numbers)[1, 2, 3]Eén 2 is overgebleven. De lijst waar je doorheen loopt inkorten, brengt Pythons tellen uit pas met de posities van de lijst, waardoor sommige waarden worden overgeslagen. De oplossing is niet verwijderen, maar wat je wilt bewaren in een nieuwe lijst verzamelen.
numbers = [1, 2, 2, 3, 4]
kept = []
for n in numbers:
if n % 2 != 0:
kept.append(n)
print(kept)[1, 3]Ik heb een variabele list genoemd en nu werkt list(...) niet list is een van Pythons eigen namen. Gebruik er een met betekenis — items, prices, names.
Stap 4 van 6 — Voorspellen
Test je begrip
Het resultaat van append wordt teruggeschreven in stock. Wat wordt er afgedrukt?
stock = ["pen", "notebook"]
stock = stock.append("bag")
print(stock)- ANone
- B['pen', 'notebook', 'bag']
- C['bag']
- DEen `AttributeError`
Alleen b is veranderd. Wat drukken de twee regels af?
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)- A[1, 2, 3] [4]
- B[1, 2, 3] [1, 2, 3, 4]
- C[1, 2, 3, 4] [1, 2, 3]
- D[1, 2, 3, 4] [1, 2, 3, 4]
De lijst bevat drie waarden en er wordt om index drie gevraagd. Wat gebeurt er?
prices = [15, 60, 850]
print(prices[3])- AEen lege lijst
- BEen `IndexError` — bij drie waarden zijn de geldige indexen `0`, `1`, `2`
- CNone
- D850 — de laatste waarde
Voor antwoorden heb je een account nodig
Log in om je antwoorden te controleren
De vragen staan hierboven, en ze in je hoofd uitwerken is waar het om draait. Log in om de antwoorden, de uitleg en de hints in drie niveaus te zien.
Jouw beurt
Schrijf een bestand genaamd marks.py met een lijst van minstens zes cijfers.
Print dan:
- De hele lijst, en hoeveel cijfers erin zitten
- Het eerste en het laatste cijfer — gebruik
[-1]voor het laatste - De som en het gemiddelde, opgebouwd met een lus in plaats van met de hand ingetypt
- Een genummerde lijst met
enumerate, beginnend bij één - Een nieuwe lijst van de geslaagde cijfers (40 of hoger), en hoeveel dat er zijn
Voer dan twee experimenten uit, en beide tellen mee:
- Voeg nog een cijfer toe met
appendaan het einde en print de som opnieuw. Moest je de regel die hem uitrekent aanpassen? Zo niet, dan heb je een echte berekening geschreven. - Schrijf
backup = marks, daarnamarks.append(100), en print beide. Wat zit er inbackup? Doe nu hetzelfde met.copy()en bekijk het verschil.
Dat laatste experiment is de drie meest waardevolle minuten van dit hoofdstuk. Het één keer met eigen ogen zien betekent dat je er nooit een middag aan kwijt bent.
Stap 6 van 6
Uitdaging — de hoofdstukquiz
Tien vragen van makkelijk naar moeilijk. De laatste zijn met opzet lastig.
Log in om de quiz te maken