tutorium 06
This commit is contained in:
@ -0,0 +1,282 @@
|
|||||||
|
# Tutorium 06 - 24.11.2023
|
||||||
|
|
||||||
|
## Vorab Informationen
|
||||||
|
|
||||||
|
- Kollektiver [Discord](https://s.narl.io/s/discord-invite) 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
|
||||||
|
<details>
|
||||||
|
<summary>QR-Code:</summary>
|
||||||
|
<img src="../../src/img/discord-invite.png">
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
1. Python-Game
|
||||||
|
1. `Vec2`:
|
||||||
|
2. `add_vecs`:
|
||||||
|
3. `Item`:
|
||||||
|
4. `Snake`:
|
||||||
|
5. `Game`:
|
||||||
|
6. `turn_direction`:
|
||||||
|
7. `grow_positions`:
|
||||||
|
8. `collision`:
|
||||||
|
9. `generate_item`:
|
||||||
|
10. `pick_item`:
|
||||||
|
|
||||||
|
## Recap - Was ist neu?
|
||||||
|
|
||||||
|
### Union-Type und Type Definitionen
|
||||||
|
|
||||||
|
- neues `type` Keyword
|
||||||
|
- mit `|` lassen sich Union-Types definieren
|
||||||
|
|
||||||
|
```py
|
||||||
|
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:
|
||||||
|
|
||||||
|
```py
|
||||||
|
def some_func[T](some_list: list[T]) -> T:
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
kleines Beispiel von "Bounds" aus Rust:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn some_func<T: Add>(some_list: Vec<T>) -> T {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
oder noch schöner
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn some_func<T>(some_list: Vec<T>) -> T
|
||||||
|
where T: Add<Output = T> + Default,
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
@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()
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
type Optional[T] = T | None
|
||||||
|
```
|
||||||
|
Python ist nicht statisch typisiert (statically-typed)! Bedeutet trotz annotation könnt ihr machen was ihr wollt:
|
||||||
|
|
||||||
|
```py
|
||||||
|
def add(x: int, y: int) -> int:
|
||||||
|
return x + y
|
||||||
|
|
||||||
|
add(2, 2.0) # 4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
genauso ist es bei Generics:
|
||||||
|
|
||||||
|
```py
|
||||||
|
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
|
||||||
|
|
||||||
|
```python
|
||||||
|
@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
|
||||||
|
|
||||||
|
```python
|
||||||
|
@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`
|
||||||
|
```py
|
||||||
|
type Point[T] = Point1D[T] | Point2D[T] | Point3D[T]
|
||||||
|
```
|
||||||
|
- Nun können wir den Type Alias mit Pattern-Matching auf den eigentlichen Datentypen reduzieren
|
||||||
|
```py
|
||||||
|
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 auszugeben
|
||||||
|
```py
|
||||||
|
match 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!
|
||||||
|
```py
|
||||||
|
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!")
|
||||||
|
```
|
||||||
|
- Guards
|
||||||
|
|
||||||
|
```py
|
||||||
|
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*!
|
||||||
|
```py
|
||||||
|
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
|
||||||
|
|
||||||
|
```py
|
||||||
|
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 Eintrags `value` beinhaltet und einen Verweis auf das nächste Element `next` in der Liste
|
||||||
|
- Um dann einen Eintrag `x` zu finden muss man nur `x`-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
|
||||||
|
|
||||||
|
```py
|
||||||
|
@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
|
||||||
|
```
|
||||||
|
|
||||||
|
131
Tutorium/tut06/src/mylist.py
Normal file
131
Tutorium/tut06/src/mylist.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Element[T]:
|
||||||
|
value: T
|
||||||
|
next: 'Element[T] | None'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MyList[T]:
|
||||||
|
head: Element[T] | None = None
|
||||||
|
last: Element[T] | None = None
|
||||||
|
length: int = 0
|
||||||
|
|
||||||
|
def push_back(self, value: T):
|
||||||
|
if not self.head:
|
||||||
|
self.head = Element(value, None)
|
||||||
|
self.last = self.head
|
||||||
|
self.length += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
self.last.next = Element(value, None)
|
||||||
|
self.last = self.last.next
|
||||||
|
self.length += 1
|
||||||
|
|
||||||
|
def remove(self, index: int) -> T | None:
|
||||||
|
if self.length <= index:
|
||||||
|
return
|
||||||
|
|
||||||
|
if index == 0:
|
||||||
|
self.head = self.head.next
|
||||||
|
self.last = self.head
|
||||||
|
self.length -= 1
|
||||||
|
|
||||||
|
i = index
|
||||||
|
previous = self.head
|
||||||
|
current = self.head
|
||||||
|
while current and i > 0:
|
||||||
|
previous = current
|
||||||
|
current = current.next
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
if i != 0 or not current:
|
||||||
|
return
|
||||||
|
if not current.next:
|
||||||
|
self.last = previous
|
||||||
|
previous.next = current.next
|
||||||
|
self.length -= 1
|
||||||
|
return current.value
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
output = ""
|
||||||
|
current = self.head
|
||||||
|
while current:
|
||||||
|
output += str(current.value)
|
||||||
|
if current.next:
|
||||||
|
output += ", "
|
||||||
|
current = current.next
|
||||||
|
return f"[{output}]"
|
||||||
|
|
||||||
|
def __getitem__(self, index: int) -> T | None:
|
||||||
|
if self.length <= index:
|
||||||
|
return
|
||||||
|
|
||||||
|
current = self.head
|
||||||
|
i = index
|
||||||
|
|
||||||
|
while current and i > 0:
|
||||||
|
current = current.next
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
if i == 0 and current:
|
||||||
|
return current.value
|
||||||
|
|
||||||
|
def __setitem__(self, index: int, new_value: T):
|
||||||
|
if self.length <= index:
|
||||||
|
return
|
||||||
|
|
||||||
|
current = self.head
|
||||||
|
i = index
|
||||||
|
|
||||||
|
while current and i > 0:
|
||||||
|
current = current.next
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
if i == 0 and current:
|
||||||
|
current.value = new_value
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
return self.length
|
||||||
|
|
||||||
|
def __iter__(self) -> 'MyList.Iter[T]':
|
||||||
|
return MyList.Iter(self.head)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Iter[E]:
|
||||||
|
current: Element[E]
|
||||||
|
|
||||||
|
def __next__(self) -> E:
|
||||||
|
if not self.current:
|
||||||
|
raise StopIteration
|
||||||
|
val = self.current.value
|
||||||
|
self.current = self.current.next
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
lst: MyList[int] = MyList()
|
||||||
|
lst.push_back(0)
|
||||||
|
lst.push_back(3)
|
||||||
|
lst.push_back(2)
|
||||||
|
assert lst[0] == 0 and lst[1] == 3 and lst[2] == 2
|
||||||
|
print(lst)
|
||||||
|
lst.remove(1)
|
||||||
|
assert lst[0] == 0 and lst[1] == 2
|
||||||
|
print(lst)
|
||||||
|
lst[1] = 3
|
||||||
|
assert lst[0] == 0 and lst[1] == 3 and len(lst) == 2
|
||||||
|
print(lst)
|
||||||
|
assert lst.remove(1)
|
||||||
|
assert lst.last.value == 0
|
||||||
|
lst.remove(0)
|
||||||
|
assert len(lst) == 0
|
||||||
|
assert not lst.head
|
||||||
|
assert not lst.last
|
||||||
|
|
||||||
|
for i in range(0, 10):
|
||||||
|
lst.push_back(i)
|
||||||
|
for it in lst:
|
||||||
|
print(it)
|
20
Tutorium/tut06/src/old/problem_with_none.py
Normal file
20
Tutorium/tut06/src/old/problem_with_none.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from union_types import FilteredList, is_even
|
||||||
|
|
||||||
|
|
||||||
|
class PrintableInt(int):
|
||||||
|
def print(self):
|
||||||
|
print(self)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
lst: FilteredList[PrintableInt] = FilteredList(filter=is_even)
|
||||||
|
lst.append(PrintableInt(0))
|
||||||
|
lst.get(0).print()
|
||||||
|
match lst.get(1):
|
||||||
|
case None:
|
||||||
|
pass
|
||||||
|
case PrintableInt(n):
|
||||||
|
n.print()
|
||||||
|
|
||||||
|
lst.append(PrintableInt(2))
|
||||||
|
lst.get(1).print()
|
39
Tutorium/tut06/src/old/union_types.py
Normal file
39
Tutorium/tut06/src/old/union_types.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
type Optional[T] = T | None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FilteredList[E]:
|
||||||
|
lst: list[E]
|
||||||
|
filter: Callable[[E], bool]
|
||||||
|
|
||||||
|
def __init__(self, filter=lambda _: True):
|
||||||
|
self.lst = []
|
||||||
|
self.filter = filter
|
||||||
|
|
||||||
|
def append(self, item: E):
|
||||||
|
if self.filter(item):
|
||||||
|
self.lst += [item]
|
||||||
|
|
||||||
|
def get(self, index: int) -> Optional[E]:
|
||||||
|
if index < len(self.lst):
|
||||||
|
return self.lst[index]
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return str(self.lst)
|
||||||
|
|
||||||
|
|
||||||
|
def is_even(n: int) -> bool:
|
||||||
|
return n % 2 == 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filter_list = FilteredList(filter=is_even)
|
||||||
|
filter_list.append(0)
|
||||||
|
print(filter_list)
|
||||||
|
filter_list.append(2)
|
||||||
|
print(filter_list)
|
||||||
|
filter_list.append(3)
|
||||||
|
print(filter_list)
|
53
Tutorium/tut06/src/points.py
Normal file
53
Tutorium/tut06/src/points.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Point1D[T]:
|
||||||
|
x: T
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Point2D[T]:
|
||||||
|
x: T
|
||||||
|
y: T
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Point3D[T]:
|
||||||
|
x: T
|
||||||
|
y: T
|
||||||
|
z: T
|
||||||
|
|
||||||
|
|
||||||
|
type Point[T] = Point1D[T] | Point2D[T] | Point3D[T]
|
||||||
|
|
||||||
|
|
||||||
|
def print_point[T](pt: Point[T]) -> None:
|
||||||
|
match 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!")
|
||||||
|
|
||||||
|
|
||||||
|
def match_list(some_list: list[str]) -> None:
|
||||||
|
match some_list:
|
||||||
|
case ["🤡", *other]:
|
||||||
|
print(f"your list starts with 🤡 and the rest is {other}")
|
||||||
|
case _:
|
||||||
|
print("your list doesn't start with 🤡")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print_point(Point1D(1)) # (1)
|
||||||
|
print_point(Point2D(1, 2)) # (1, 2)
|
||||||
|
print_point(Point3D(1, 2, 3)) # (1, 2, 3)
|
||||||
|
print_point(Point3D(0, 0, 0)) # (1, 2, 3)
|
||||||
|
print_point("not a point") # Not a point!
|
||||||
|
match_list(["🤡", "ich", "hasse", "python", "manchmal"])
|
8
Tutorium/tut06/src/problem_with_annotations.py
Normal file
8
Tutorium/tut06/src/problem_with_annotations.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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]
|
BIN
src/img/discord-invite.png
Normal file
BIN
src/img/discord-invite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
Reference in New Issue
Block a user