# Unterschiede zu Vorjahren ## Typalias Manchmal braucht man eine Kombination aus Datentypen ### Vorher Erstellen von Typaliasen mit `Union` ```python from typing import Union # `RealNumber` ist also eine `int` oder eine `float` RealNumber = Union[int, float] # also geht hier # `add(1, 1) -> 2` # `add(1.0, 1.0) -> 2.0` # aber auch # `add(1, 1.0) -> 2.0` def add(x: RealNumber, y: RealNumber) -> RealNumber: return x + y ``` ### In Python3.12 Wir haben jetzt das `type`-Keyword für das wir nichts importieren müssen und können einfach Datentypen mit `|` *odern* ```python # `type` statt `Union` type NewRealNumber = int | float # sonst sieht alles gleich aus def new_add(x: NewRealNumber, y: NewRealNumber) -> NewRealNumber: return x + y ``` ## Generiche Typ-Parameter (Generics, Typvariabeln, ...) ### Vorher Erstellen von generischen Typvariabeln/Typ-Parametern mit `TypeVar` aus `typing`: ```python from typing import Generic, TypeVar, Optional from dataclasses import dataclass, field # T ist unser Platzhalten für einen beliebigen Datentyp T = TypeVar('T') # jetzt können wir `T` einfach als Platzhalter für einen beliebigen Datentyp verwenden # praktisch weil wir so wissen dass `x`, `y` und der `return` zumindest den **selben** Datentyp verwenden # aber welcher genau wissen wir nicht bis jemand die Funktion mit z.b. `int` oder `float`! # Zum Beispiel bedeutet dass # `add(1, 2) == 3` => `T == int` # `add(1.0, 2.0) == 3.0` => `T == float` # ... def add(x: T, y: T) -> T: return x + y # Ebenso können wir `T` auch für Klassen verwenden @dataclass # wir müssen hier festlegen dass T in der Klasse verwendet wird # indem `MyList` von `Generic[UnsereGenerischeVariabel]` erbt class MyList(Generic[T]): def __post_init__(self, ): # wir haben intern eine `internal_list` die von außen nicht sichtbar ist self.__internal_list: list[T] = field(default_factory=list) # hier speichern wir uns die aktuelle Länge unserer Liste self.__length: int = 0 def append(self, value: T): """fügt ein element mit dem Typ `T` hinzu""" self.__internal_list += [value] self.__length += 1 def remove(self, value: T): """entfert das element `value`, wenn es in unserer Liste ist""" if value in self.__internal_list: self.__internal_list.remove(value) def get(self, i: int) -> Optional[T]: """gibt das element an `i` Stelle zurück. Wenn Liste kleiner als `i` ist, dann `None`""" if i < len(self): return self[i] return None def __len__(self) -> int: return self.__length def __getitem__(self, i: int) -> T: return self.__internal_list[i] ``` ### In Python3.12 Deklaration der Typvariabel direkt im Funktionskopf/Klassenkopf mit `[]`. Sprich wir brauchen `TypeVar` nicht mehr ```python from typing import Optional from dataclasses import dataclass, field # hier wird `V` durch `[V]` deklariert und kann in der Funktion benutzt werden def new_add[V](x: V, y: V) -> V: return x + y # hier wird `T` für die Klasse `NewMyList` deklariert durch `[T]` und kann überall in der Klasse genutzt werden # der Rest ist genau gleich, das einzige was sich eben geändert hat ist dass man dieses `TypeVar` nicht mehr braucht # sondern direkt in der Deklaration von Klassen/Funktionen/Methoden die generischen Typvariabeln mit deklariert @dataclass class NewMyList[T]: def __post_init__(self, ): self.__internal_list: list[T] = field(default_factory=list) self.__length: int = 0 def append(self, value: T): self.__internal_list += [value] self.__length += 1 def remove(self, value: T): if value in self.__internal_list: self.__internal_list.remove(value) def get(self, i: int) -> Optional[T]: if i < len(self): return self[i] return None def __len__(self) -> int: return self.__length def __getitem__(self, i: int) -> T: return self.__internal_list[i] ```