← Python/Interfaces en typing
Les 4 van 8
MODULE 08 · 04◷ 30 min

Typeverbanden met generieke functies beschrijven

Na deze les kun je
  • Een echte typeparameter in een functiedefinitie gebruiken.
  • Invoer- en uitvoertypen via dezelfde typeparameter verbinden.
  • Annotaties onderscheiden van conversie, kopiëren en terugvalgedrag.

Een functie die het eerste element van een lijst teruggeeft, hoeft dat element meestal niet te veranderen. Bij getallen krijg je een getal terug; bij objecten krijg je datzelfde object. Met een generieke functie kun je die relatie beschrijven zonder voor iedere elementsoort een aparte functie te schrijven.

Een typeverband een naam geven

Een generieke functie gebruikt een typeparameter om een verband tussen typen te beschrijven. In de gepinde Pythonversie kun je die parameter direct achter de functienaam schrijven: def first[T](items: list[T]) -> T:. De naam T staat voor het elementtype dat bij de betreffende toepassing hoort.

list[T] en het resultaat T verwijzen naar dezelfde typeparameter. De functie zegt dus meer dan alleen dat zij iets teruggeeft: het resultaat hoort bij het elementtype van de invoer. De berekening zelf kan nog steeds simpelweg items[0] gebruiken.

Het eerste voorbeeld gebruikt dit verband bij één ontvangen waarde: de invoer en het resultaat hebben beide typeparameter T. De functie geeft die waarde rechtstreeks door.

In deze les blijft T onbeperkt: schrijf [T] zonder een bound of lijst van toegestane typen. De functies moeten hun typeverband voor verschillende soorten waarden beschrijven.

Eén typeparameter verbindt een waarde met haar resultaat
def unchanged[T](value: T) -> T:
    return value

print(unchanged(4))
marker = object()
print(unchanged(marker) is marker)
print(unchanged.__type_params__[0].__name__)
Verwachte uitvoer
4
True
T

De vierkante haken achter de definitienaam zijn geen extra waardeparameter voor de aanroep. Je gebruikt gewoon first(values). In de voorbeelden zie je naast het resultaat ook de echte typeparametermetadata van de functie.

Een annotatie maakt geen kopie

Een typeparameter verandert een waarde niet en roept ook geen constructor aan. Geeft first een object uit de lijst terug, dan blijft dat precies dezelfde instantie. Gebruik dus geen str(...), int(...) of nieuwe instantie om een resultaat passend te laten lijken.

Bij een lege lijst blijft het gewone probleem bestaan: er is geen eerste element. De eerste opdracht laat daarom de echte IndexError van de lijstindexering doorlopen. Een generieke annotatie bedenkt geen ontbrekendewaarde en voegt geen terugvalgedrag toe.

Hetzelfde verband binnen een tuple

Ook een resultaat met meerdere onderdelen kan een typeverband beschrijven. tuple[T, T] betekent hier een tuple van twee waarden met hetzelfde bedoelde type. De namen van de parameters blijven gewone waardenamen, zoals left en right.

De opdracht pair bewaart beide ontvangen objecten in die volgorde. Zij maakt een nieuwe tuple, maar geen kopieën van de objecten erin. Twee verwijzingen naar hetzelfde ontvangen object blijven daarom twee verwijzingen naar datzelfde object.

Een nieuwe lijst met dezelfde elementen

Bij copy_items is de buitenste lijst wel nieuw. De functie beschrijft list[T] als invoer én resultaat, en maakt een ondiepe kopie. Zo'n kopie heeft een eigen lijststructuur maar bewaart de oorspronkelijke elementverwijzingen.

Een nieuwe lijst behoudt de oorspronkelijke elementverwijzingen
def with_extra[T](values: list[T], extra: T) -> list[T]:
    return [*values, extra]

original = [["a"]]
extended = with_extra(original, ["b"])
print(len(original), len(extended))
print(extended is original)
print(extended[0] is original[0])
extended[0].append("c")
print(original)
Verwachte uitvoer
1 2
False
True
[['a', 'c']]

Je herkent het onderscheid tussen alias en kopie uit Collecties. Generieke annotaties veranderen die kopieregels niet. Het blijft de echte functiebody die bepaalt welke objecten nieuw worden gemaakt en welke worden gedeeld.

Een mogelijk ontbrekende generieke waarde

present_or[T] combineert dit typeverband met de vorige les. De parameter value heeft annotatie T | None, de terugvalwaarde heeft type T en het resultaat ook. Alleen wanneer value is None wordt de terugvalwaarde gekozen.

Een aanwezige lege lijst, nul of lege tekst blijft het ontvangen object of de ontvangen waarde. Ook de gekozen terugvalwaarde blijft precies hetzelfde object. Deze functie voert geen conversies of extra kopieën uit.

Veelgemaakte fout: T als runtimebewerking behandelen

T beschrijft hier een typeverband; het is geen opdracht om T(value) uit te voeren. Python controleert de afspraak bovendien niet automatisch bij iedere aanroep. Deze les toetst echte annotaties en echt gedrag en claimt geen uitslag van een statische checker.

AAN DE SLAG 0/4
1

Een elementtype naar het resultaat doorgeven

Maak first[T](items: list[T]) -> T. Geef het eerste element ongewijzigd terug en bewaar zijn identiteit; laat bij een lege lijst de gewone IndexError van items[0] doorlopen. Gebruik één echte typeparameter T, geen vaste int- of str-annotatie.

2

Eenzelfde typeverband in een tuple bewaren

Maak pair[T](left: T, right: T) -> tuple[T, T]. Geef een tuple met left en right in die volgorde terug. Bewaar beide objectidentiteiten en maak geen kopieën of conversies.

3

Een nieuwe buitenste lijst met dezelfde elementen maken

Maak copy_items[T](items: list[T]) -> list[T]. Geef een nieuwe ondiepe lijstkopie met dezelfde volgorde en elementidentiteiten. Ook een lege lijst krijgt een nieuwe buitenste lijst; verander items niet.

4

Een generieke terugvalwaarde zonder conversie kiezen

Maak present_or[T](value: T | None, fallback: T) -> T. Geef fallback alleen bij None terug, anders value. Bewaar ook de identiteit van de gekozen objecten; nul, False, lege tekst en een lege lijst zijn aanwezige waarden.

Onthoud dit

  • Eén typeparameter verbindt de bedoelde invoer- en uitvoertypen.
  • Annotaties bepalen geen conversie, kopie of standaardwaarde.
  • Objectidentiteit blijft een afzonderlijk onderdeel van het functiecontract.

Hierna geef je een verzameling vereiste methoden een expliciete naam met een Protocol.

main.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.