6.7 KiB
Tutorium 06 - 24.11.2023
Vorab Informationen
- Kollektiver Discord mit Tutorium 05 (Daniel Mironow)
- Dani's-Tutorium: Mi 16:00 - 18:00, Geb. 106, SR 00 007
- Im Discord könnt ihr euch direkt mit uns Tutoren austauschen oder untereinander
- Invite: https://s.narl.io/s/discord-invite
- Es gibt wieder einen
Korrektur Blatt 05
- am Samstag, ich hab mich etwas vertan bei der Korrektur
- Punkteverteilung und häufige Fehler werden hier hinzugefügt
Häufige Fehler
- Type annotation
@dataclass
nicht benutzt- mutieren von erstellten Objekt
Vorrechnen
- Python-Game
Vec2
: aw616add_vecs
: fk439Item
: ln200Snake
: lp321Game
: rl173turn_direction
: ih205grow_positions
:collision
:generate_item
:pick_item
:
Recap - Was ist neu?
Union-Type und Type Definitionen
- neues
type
Keyword - mit
|
lassen sich Union-Types definieren
type Number = int | float | complex
Generics (Typvariabeln)
Manchmal weiß man nicht welcher Typ genau gemeint ist, möchte aber trotzdem "sicherstellen" dass es sich nicht um zwei unterschiedliche handelt:
def some_func[T](some_list: list[T]) -> T:
# ...
kleines Beispiel von "Bounds" aus Rust:
fn some_func<T: Add>(some_list: Vec<T>) -> T {
// ...
}
oder noch schöner
fn some_func<T>(some_list: Vec<T>) -> T
where T: Add<Output = T> + Default,
{
// ...
}
@dataclass
class Stack[T]:
internal_list: list[T]
def push(self, item: T) -> None:
self.internal_list.append(item)
def pop(self) -> T | None:
if len(self.internal_list) == 0:
return None
return self.internal_list.pop()
type Optional[T] = T | None
Python ist nicht statisch typisiert (statically-typed)! Bedeutet trotz annotation könnt ihr machen was ihr wollt:
def add(x: int, y: int) -> int:
return x + y
add(2, 2.0) # 4.0
genauso ist es bei Generics:
from mylist import MyList
lst: MyList[int] = MyList()
lst.push_back(0)
lst.push_back(1)
print(lst) # [0, 1]
lst.push_back("haha not a number")
print(lst) # [0, 1, haha not a number]
self
- mit
self
ruft man das Objekt wessen Verhalten man modelliert- damit kann das Objekt verändert (mutiert) werden
- einfache Datenklassen bekommen Objekte die ein Verhalten modellieren
- jede Methode eines Objekt bekommt
self
als ersten Parameter und gibt vor wie sich ein Objekt verhält
@dataclass
class MyNumber[T]():
number: T
def add(self, value: T) -> T:
self.number += value
return self.number
num = MyNumber(3)
print(num.add(5)) # 8
print(num.number) # 8
Pattern-Matching
Mit dem match
Keyword lassen sich verschiedene Bedingungen matchen
-
Zunächst Types:
-
Wir erstellen die Datenklassen
@dataclass class Point1D[T]: x: T @dataclass class Point2D[T]: x: T y: T @dataclass class Point3D[T]: x: T y: T z: T
-
Wir erstellen einen Typ Alias
Point
type Point[T] = Point1D[T] | Point2D[T] | Point3D[T]
-
Nun können wir den Type Alias mit Pattern-Matching auf den eigentlichen Datentypen reduzieren
def print_point[T](pt: Point[T]) -> None: match pt: case Point1D(x): print(f"Point1D: ({x})") case Point2D(x, y): print(f"Point2D: ({x}, {y})") case Point3D(x, y, z): print(f"Point3D: ({x}, {y}, {z})") case _: print("Not a point!")
-
-
Aber auch Bedingungen wie Werte
- Nun erweitern wir unsere
print_point
um Nullpunkte auszugebenmatch pt: case Point1D(0) | Point2D(0, 0) | Point3D(0, 0, 0): print("Nullpunkt!") case Point1D(x): print(f"Point1D: ({x})") case Point2D(x, y): print(f"Point2D: ({x}, {y})") case Point3D(x, y, z): print(f"Point3D: ({x}, {y}, {z})") case _: print("Not a point!")
- Achtung: Reihenfolge der Cases ist wichtig!
match pt: case Point1D(x): print(f"Point1D: ({x})") case Point2D(x, y): print(f"Point2D: ({x}, {y})") case Point3D(x, y, z): print(f"Point3D: ({x}, {y}, {z})") case Point1D(0) | Point2D(0, 0) | Point3D(0, 0, 0): print("Nullpunkt!") case _: print("Not a point!")
- Nun erweitern wir unsere
-
Guards
match pt: case Point1D(x) if x == 0: print("1-D Nullpunkt!") case Point1D(x): print(f"Point1D: ({x})") case Point2D(x, y): print(f"Point2D: ({x}, {y})") case Point3D(x, y, z): print(f"Point3D: ({x}, {y}, {z})") case _: print("Not a point!")
-
Noch mehr Types matchen!
match pt: case Point1D(int): print(f"Ganzzahliger Punkt! {pt.x}") case Point1D(float): print(f"Gleitkomma Punkt! {pt.x}") # ...
-
Und es wird immer seltsamer
match some_list: case ["🤡", *other]: print(f"your list starts with 🤡 and the rest is {other}")
Blatt 06
- Fragen?
Aufgabe: eigene Liste implementieren
Implementiere eine generische Liste mit append
, get
und remove
, ohne buildin Listen zu verwenden!
Konzept einer (einfach verketteten) Liste
- Es gibt einen Listeneintrag
Element
, der den eigentlichen Wert des Eintragsvalue
beinhaltet und einen Verweis auf das nächste Elementnext
in der Liste - Um dann einen Eintrag
x
zu finden muss man nurx
-mal die Liste ablaufen und den Wert auslesen - Wenn man ein Element hinzufügen will muss man lediglich ans Ende der Liste laufen und ein neuen Eintrag erstellen
- Wenn man ein Element entfernen will muss man lediglich das nächste Element vom vorherigen auf das nächste Element vom zu entfernenden setzen
Hilfestellung
@dataclass
class Element[T]:
value: T
next: 'Element[T] | None'
class MyList[T]:
head: Element[T] | None
length: int
def append(self, value: T) -> None:
pass
def get(self, index: int) -> T | None:
pass
def remove(self, index: int) -> T | None:
pass