1.2 Variabelen en types

Een waarde bewaren onder een naam, en weten welke soort waarde het is

OpmerkingLeerdoelen

Na deze les kan je:

  • de twee stappen van een toekenning in de juiste volgorde uitleggen
  • voorspellen welke waarde een variabele bevat na een reeks toekenningen
  • het type van een waarde bepalen en benoemen
  • een waarde omzetten met str(), int(), bool() en float(), en zeggen wanneer dat misloopt

De vorige les eindigde bij een programma dat alles onmiddellijk vergeet. Elke uitdrukking leverde een waarde op, en die waarde verdween zodra de volgende regel aan de beurt kwam. In deze les leer je een waarde bewaren onder een naam, zodat een latere regel er nog iets mee kan doen. Dat is de stap waarmee een programma iets kan opbouwen.

1 Waarom een programma moet onthouden

Stel dat je de rekening van een winkelmandje wil uitrekenen. Je kent de prijzen pas één voor één, en je wil het totaal zien wanneer het laatste artikel gepasseerd is. Met de middelen uit de vorige les lukt dat niet, aangezien elke uitdrukking haar waarde meteen weer kwijt is.

Een variabele lost dat op. Het is een stukje geheugen met een naam, waarin een programma een waarde bewaart om ze later terug te gebruiken.

Vanaf die regel duidt de naam leeftijd het getal \(17\) aan, en overal waar je leeftijd schrijft, staat er in feite dat getal.

3 De regel x = x + 1

In de wiskunde heeft de vergelijking \(x = x + 1\) geen enkele oplossing, want

\[ \{\, x \in \mathbb{R} \mid x = x + 1 \,\} = \emptyset . \]

Toch is x = x + 1 een volstrekt normale Python-regel. Dat komt doordat de twee stappen, de rechterkant uitrekenen en het resultaat toekennen aan de linkerkant, na elkaar gebeuren. Op het ogenblik dat de rechterkant uitgerekend wordt, heeft x nog zijn oude waarde.

Om zo’n reeks regels te volgen, schrijf je een trace table: één kolom per variabele, één rij per regel, en in elke rij wat de variabele op dat ogenblik bevat.

regel x
x = 5 5
x = x + 1 6

De tweede rij gebruikt de waarde uit de eerste. Rechts staat dus \(5 + 1\), en de uitkomst \(6\) komt onder dezelfde naam terecht.

Deze constructie komt zo vaak voor dat Python er een afkorting voor heeft.

voluit afgekort
x = x + 1 x += 1
totaal = totaal + prijs totaal += prijs

Daarmee laat een programma een waarde aangroeien. Zo’n variabele heet een accumulator: ze begint bij een beginwaarde, en elke volgende regel voegt iets bij.

In een trace table is te zien dat elke regel verdergaat waar de vorige gestopt is.

regel totaal
totaal = 0 0
totaal += 12 12
totaal += 5 17
totaal += 3 20

Het winkelmandje uit de eerste sectie is daarmee opgelost, zij het dat je voorlopig voor elk artikel een regel moet schrijven.

4 b = a neemt de waarde over

Twee namen kunnen dezelfde waarde aanduiden, en het is de moeite waard om na te gaan wat er dan met de tweede gebeurt wanneer de eerste verandert.

Er verschijnt 3. Met twee variabelen krijgt de trace table een kolom per naam, en dan is meteen te zien waarom.

regel a b
a = 3 3
b = a 3 3
a = 10 10 3

Bij b = a krijgt b de waarde die a op dat ogenblik heeft, en meer gebeurt er niet. Die regel is een gewone toekenning: rechts wordt de waarde van a opgezocht, en die uitkomst wordt toegekend aan b. Wat er daarna met a gebeurt, heeft geen effect op b.

5 Dezelfde bewerking, een andere uitkomst

Wat levert 2 + 2 op, en wat levert "2" + "2" op? Het enige verschil zijn de aanhalingstekens.

Er verschijnt 4, dan 22, dan hahaha. Het teken + telt getallen op en plakt teksten aan elkaar, en * vermenigvuldigt getallen en herhaalt een tekst. Wat een bewerking doet, hangt bijgevolg af van de soort waarden waarop ze werkt.

6 De vier types

De soort van een waarde heet haar type. Voorlopig volstaan er vier.

type waarvoor voorbeelden
int gehele getallen 42, 0, -7
float kommagetallen 2.4, -0.5, 3.14
str tekst, tussen aanhalingstekens "hallo", "42", ""
bool waar of onwaar True, False

Tekst tussen aanhalingstekens heet een string. 42 en "42" zien er op je scherm bijna hetzelfde uit: het eerste is een getal waarmee je kan rekenen, het tweede een tekstje dat toevallig uit cijfertekens bestaat.

Een bool heeft maar twee mogelijke waarden, True en False. Zo’n waarde is doorgaans het resultaat van een vergelijking, en de basis waarop een keuze wordt gemaakt.

Er verschijnt True, dan False. Een vergelijking levert dus een waarde op, net zoals een optelling dat doet.

De functie type() zegt je welk type een waarde heeft, wat nuttig is zodra je niet meer zeker bent van wat je in handen hebt.

Het antwoord komt in de vorm <class 'int'>.

De termen class en type stonden oorspronkelijk voor twee verschillende zaken. Python 3 heeft dat onderscheid opgeheven, en sindsdien zijn ze synoniem.

7 Wanneer Python weigert

Blijft de vraag wat er gebeurt wanneer je de twee soorten mengt. Levert 2 + "2" het getal 4 op, de tekst "22", of nog iets anders?

Python meldt een TypeError en stopt. Het weet niet of je \(4\) bedoelt of "22". Dat is beter zo, aangezien een taal die hier zomaar een keuze maakt, die keuze eveneens maakt op de plaatsen waar jij ze niet verwacht.

De foutmelding zelf is leesbaar: unsupported operand type(s) for +: 'int' and 'str' noemt de bewerking en de twee types die niet samengaan. Lees bij een vastgelopen programma altijd eerst het laatste regeltje van de melding.

8 Omzetten

Om van het ene type naar het andere te gaan bestaan er omzetfuncties. Ze geven een nieuwe waarde terug en laten de oude ongemoeid.

omzetting functie voorbeeld
naar tekst str() str(42) geeft "42"
naar geheel getal int() int("42") geeft 42
naar kommagetal float() float("2.5") geeft 2.5

Daarmee is de gebruikelijke eerste struikelsteen opgelost, namelijk een getal aan een zin plakken.

WaarschuwingOpgelet

int() werkt enkel op tekst die werkelijk een geheel getal voorstelt. int("3.5") loopt vast, en int("drie") eveneens. Daarnaast levert een deling met / altijd een float op, ook wanneer de uitkomst rond is, dus 10 / 2 geeft 5.0 en niet 5. Wil je een geheel getal overhouden, gebruik dan //.

9 Conclusie

Een variabele bewaart één waarde onder een naam, en een toekenning vult die waarde in. Eerst wordt de rechterkant uitgerekend, daarna wordt de uitkomst links bewaard. Doordat die twee stappen na elkaar komen, mag dezelfde naam aan beide kanten staan, en dat is precies wat een programma laat aangroeien.

Elke waarde die je zo bewaart, heeft bovendien een type, en dat type bepaalt wat een bewerking betekent. Hetzelfde teken + telt op of plakt aan elkaar, naargelang wat er links en rechts van staat, en waar de combinatie geen betekenis heeft, weigert Python te raden.

Wat je nu kan opschrijven, blijft evenwel even lang als het aantal stappen dat je wil zetten. Een mandje met twintig artikelen vergt twintig regels, en een teller die tot honderd loopt honderd. In een volgende les leer je hoe je zo’n reeks regels vervangt door twee.

10 Begrippen

  • variabele (variable): een stukje geheugen met een naam, waarin een programma een waarde bewaart om ze later terug te gebruiken.
  • toekenning (assignment): het bewaren van een waarde onder een naam met =, in twee stappen: eerst de rechterkant uitrekenen, dan links bewaren.
  • overschrijven (to overwrite): een nieuwe toekenning aan dezelfde naam, waarbij de vorige waarde verdwijnt.
  • trace table: een tabel met één kolom per variabele en één rij per regel, die laat zien wat elke variabele op dat ogenblik bevat.
  • += (augmented assignment): de afkorting van een toekenning waarvan de rechterkant met de oude waarde begint.
  • accumulator: een variabele die een waarde stap na stap opbouwt, en die daarom vóór de eerste stap een beginwaarde krijgt.
  • type (type): de soort van een waarde, die bepaalt wat een bewerking ermee doet.
  • string (string): tekst tussen aanhalingstekens, in Python str.
  • bool (boolean): het type met de twee waarden True en False.
  • omzetfunctie (conversion function): een functie die een waarde van het ene type naar het andere brengt, zoals str() en int().

11 Oefeningen

Een oefencel staat op zichzelf, dus wat je in een vorige cel schreef, kent Python hier niet meer.

  1. Bepaal telkens wat er getoond wordt, zonder de computer. Maak bij de eerste een trace table.

    1. p = 4, dan p = p * p, dan print(p)
    2. n = 10, dan n += 5, dan n += 5, dan print(n)
    3. u = 2, dan v = u + 3, dan u = 100, dan print(v)
    4. "7" * 3 en int("7") * 3
    5. "1" + 1
    6. print(type(3.5)) en print(type("3.5"))
    7. str(2) + str(2), en daarna int("2.5") + 1
    8. k = 2, dan k = k + k, dan k += k, dan print(k)

    a. 16.

    regel p
    p = 4 4
    p = p * p 16

    b. 20. Elke += telt vijf bij de waarde van dat ogenblik, dus \(10\) wordt \(15\) en daarna \(20\).

    c. 5. Bij v = u + 3 wordt de rechterkant uitgerekend met de waarde die u dan heeft, namelijk \(2\). De latere toekenning aan u laat v ongemoeid.

    d. "777" en 21. In het eerste geval staat er een string die driemaal herhaald wordt. In het tweede levert int("7") het getal \(7\) op, waarna er gewoon vermenigvuldigd wordt.

    e. Een TypeError. Er staat links tekst en rechts een getal, en Python kiest niet in jouw plaats. Bedoel je "11", schrijf dan "1" + str(1), en bedoel je \(2\), schrijf dan int("1") + 1.

    f. <class 'float'> en <class 'str'>. De aanhalingstekens maken het verschil, en het punt in "3.5" verandert daar niets aan.

    g. "22" en een ValueError. Beide getallen worden eerst tekst, waarna + die twee aan elkaar plakt. In het tweede geval stelt "2.5" geen geheel getal voor, dus loopt int() vast. Met float("2.5") + 1 krijg je wel een uitkomst, namelijk 3.5.

    h. 8. De twee schrijfwijzen doen hetzelfde, dus de waarde verdubbelt twee keer.

    regel k
    k = 2 2
    k = k + k 4
    k += k 8
  2. Vervang elke ______ door code.

    Rechts van += mag een naam staan. Gebruik de twee namen die hierboven al een waarde gekregen hebben.

  3. Hieronder moet x eerst verdubbeld worden en daarna met één verhoogd, elk op een eigen regel.

    Elke regel rekent eerst de rechterkant uit met de waarde die x op dat ogenblik heeft. Na de eerste regel is x dus al veranderd.

  4. De twee waarden hieronder zijn strings. Zorg dat er 42 verschijnt en niet 1230.

    Zet beide waarden eerst om naar een geheel getal.

  5. Toon met de twee namen hieronder de zin 3 boeken kosten 24 euro, zonder de getallen 3 en 24 zelf op te schrijven.

    Het totaal reken je uit met de twee namen. Alles wat in de zin terechtkomt, moet eerst tekst worden.

  6. Verwissel de twee waarden hieronder, zodat er eerst 8 verschijnt en daarna 3. De twee regels print blijven staan zoals ze zijn.

    Zodra je links = rechts schrijft, is de oude waarde van links nergens meer te vinden. Bewaar ze eerst onder een derde naam.

  1. Verwissel dezelfde twee waarden opnieuw, maar nu met drie toekenningen en zonder een derde naam. Je mag enkel optellen en aftrekken.

    Zet in links eerst de som van de twee. Uit die som en één van beide waarden haal je de andere terug.

    regel links rechts
    links = 3 3
    rechts = 8 3 8
    links = links + rechts 11 8
    rechts = links - rechts 11 3
    links = links - rechts 8 3

    De som blijft in links bewaard tot beide waarden er weer uit gehaald zijn.

  2. De regel hieronder is een geldige instructie. Python aanvaardt ze zonder één foutmelding. Wat is het resultaat, en waarom zou je zoiets zelf niet schrijven?

    Twee regels die je nog niet kent en die je hier nodig hebt.

    Een bool telt in een berekening mee als een getal, waarbij True voor 1 staat en False voor 0.

    bool() van eender welke waarde is True, op 0 en de lege string "" na. Enkel of er iets staat telt, niet wat er staat.

    Er verschijnt 4.0.

    stap uitkomst
    int(True) 1
    bool("abc") True
    1 + True 2
    float(2) 2.0
    2 + 2.0 4.0
    bool(7) True
    4.0 * True 4.0

    Het is 4.0 en niet 4, aangezien één kommagetal in een berekening volstaat om de uitkomst een kommagetal te maken.

    Python weigert 2 + "2", maar tussen de getaltypes onderling is het erg meegaand, en dat is precies wat deze regel uitbuit. Dat ze aanvaard wordt, maakt er nog geen goede code van. bool("False") is True, aangezien enkel telt of er iets staat, en een True die stilzwijgend als 1 meetelt, valt in een lange berekening niet meer op. Gebruik int() en float() waar je werkelijk van type verandert, en laat True en False staan waar ze thuishoren, in een vergelijking.

    Nog zoiets: de Opgelet bij Omzetten zegt dat int("3.5") vastloopt. Op het getal 3.5 werkt int() wel, en dan valt alles na de komma weg, dus int(3.5) geeft 3. Het verschil zijn de aanhalingstekens.