added tut11

This commit is contained in:
2024-01-12 08:23:11 +01:00
parent cdd5008d65
commit 60d3bab464
3 changed files with 391 additions and 16 deletions

View File

@ -3,13 +3,13 @@ marp: true
paginate: true paginate: true
class: invert class: invert
# theme: uncover # theme: uncover
footer: Tutorium 11 - 15.01.2023 - Nils Pukropp - https://s.narl.io/s/tutorium-11 footer: Tutorium 11 - 12.01.2023 - Nils Pukropp - https://s.narl.io/s/tutorium-11
header: header:
--- ---
# Tutorium 11 - EidP 2024 # Tutorium 11
Dictionary, List-Comprehensions, Funktionen als Objekte Dictionary, List-Comprehensions, OOP nochmal
--- ---
@ -22,7 +22,7 @@ Dictionary, List-Comprehensions, Funktionen als Objekte
--- ---
### Creating a Dictionary ## Creating a Dictionary
```python ```python
dictionary = { dictionary = {
@ -35,23 +35,347 @@ dictionary = {
--- ---
### Creating a Dictionary ## Creating a Dictionary - Beispiel
- `Key`: Modul als `str` referenziert
- **immutable**
- `Value`: Liste aller Stundenten, mutable
- **mutable**, wir können Stunden entfernen/hinzufügen
```python ```python
dictionary = { courses: dict[str, list[str]] = {
<key>: <value>, "eidp": ["np19", "az34", "jf334"],
<key>: <value>, "mathe": ["aw331", "pl67"],
... "sdp": ["xy111", "xz112"],
<key>: <value>
} }
``` ```
#### Beispiel ---
## Was passiert wenn wir eine `list` als nehmen?
- `list[Any]` ist mutable, genauer nicht **hashable**
- `hash([1, 2, 3])` wirft einen Error
- `dict` nutzt `hash(...)` für lookups
- `dict[list[Any], Any]` wirft also einen `TypeError`, weil `list` nicht **hashable** ist
- `tuple` sind immutable, wenn dessen Elemente immutable sind
- z.B. `(1, 2)`
---
## Dictionary indizieren
```python ```python
courses = { print(courses["eidp"]) # ["np19", "az34", "jf334"]
"eidp": ["np163", "az34", "jf334"],
"mathe": ["aw331", "pl67"], courses["eidp"] += ["jk331"]
"sdp": []
print(courses["eidp"]) # ["np19", "az34", "jf334"]
courses["mathe_2"] += ["jk331"] # KeyError
courses["mathe_2"] = ["jk331"] # fügt "mathe_2" hinzu mit dem Wert ["jk331"]
if "logik" not in courses:
print("logik is not in courses!")
courses["logik"] = []
print("now it is!")
```
---
## Was kann man als Value verwenden?
---
## Was kann man als Value verwenden?
**Alles**!
```python
ops: dict[str, Callable] = {
'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x / y,
} }
```
print(ops['+'](3, 1)) # 4
```
---
## Dictionary iterieren
- mit der `items()` Methode bekommt man jeden `Key` mit `Value`
```python
for courses, students in courses.items():
print(f"{courses}: {students}")
```
---
## List-Comprehension
- hattet ihr noch nicht in der Vorlesung
- Viel zu Viele nutzen es schon, und ich will keine 0 Punkte geben
- Syntax-Sugar für das erstellen von Listen basierend auf anderen Listen
```python
even_numbers = [number for number in range(101) if number % 2 == 0] # [0, 2, ..., 100]
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
all_permutations = [(a, b) for a in range(3)
for b in range(3)]
# quiet fast actually (for python)
pythagorean_triples = [(a, b, c) for a in range(101)
for b in range(101)
for c in range(101)
if a ** 2 + b ** 2 == c ** 2]
```
---
## List-Comprehension
```python
# ew no syntax sugar
all_students: set[str] = set()
for _, students in courses.items():
for student in students:
all_students.add(student)
# syntax sugar!
print({student for students in courses.values()
for student in students})
# flattening stuff
matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
print([num for row in matrix for num in row]) # [1, 0, 0, 0, 1, 0, 0, 0, 1]
```
---
## Übertreibt es aber nicht!
- List-Comprehensions sind zum *erstellen* von Listen.
- List-Comprehensions sollten **nichts** *machen*
Also **kein** side effects oder Funktionsaufrufe!
```python
x = [1, 2, 3, 4]
[x.append(num) for num in range(5, 11)] # really bad
```
```python
def f(x: float) -> float:
return x ** 2 + 3 * x + 1
[f(x) for x in range(101)] # bad
```
---
## OOP - Funktion oder Methode?
```python
import math
from dataclasses import dataclass
@dataclass
class Position:
x: float
y: float
def distance(self, other: Position) -> float:
return math.sqrt((other.x - self.x) ** 2 + (other.y - self.y) ** 2)
def distance_of(a: Position, b: Position) -> float:
return math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2)
```
---
## OOP - Funktion oder Methode!
- `distance(self, other: Position)` ist eine Methode.
- Gehört zu einer Klasse und hat `self` als Parameter
- `distance_of(a: Position, b: Position)` ist eine Funktion.
- unabhängig (normalerweise kein **state**)
---
## Was ist ein **State** (Zustand)?
```python
class GameState(Enum):
RUNNING = auto()
PAUSED = auto()
ENDED = auto()
@dataclass
class Game:
state: GameState
```
- Unser `Game`-Objekt hat einen Zustand der sich ändern kann
- Unser `Game` kann pausiert, beendet oder am Laufen sein
- Dieser Zustand kann sich ändern
---
## Was ist ein **State** (Zustand)?
```python
@dataclass
class Position:
x: float
y: float
```
Ebenso sind `x` und `y` Zustände von `Position`, wenn auch nicht ganz so offensichtlich.
- Beschreiben das Objekt
- Können sich ändern
---
## Was ist ein **State** (Zustand)?
Wie sieht es mit `distance_of(...)` aus?
```python
def distance_of(a: Position, b: Position) -> float:
return math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2)
```
- Verhält sich immer gleich
- also hat keinen **State**
- ändert keine **States**
- manchmal passiert das leider, ist aber ein schlechter Stil!
```python
def move_to(pos: Position, x: float, y: float):
pos.x = x
pos.y = y
```
- Guter Stil ist es eigentlich immer die Parameter in Ruhe zu lassen!
---
## Funktionen mit **State**
- Ihr kriegt 0 Punkte für die gesamte Abgabe.
```python
can_execute = True
def function(x: int) -> int:
global can_execute
if can_execute:
can_execute = False
return x + 1
return x
def can_execute_again():
global can_execute
can_execute = True
```
- Ich meine das ernst mit den 0 Punkten
---
## Dazu zählt auch sowas!
```python
def main():
session = Session()
def do_something():
session.do()
# ...
```
---
## Private, Getter, Setter
Bei `@dataclass`:
- `InitVar` verwenden wenn im Klassenrumpf deklariert
- Sollen mit `_<variable_name>` benannt werden
- `__post_init__(self, <variable>)` muss definiert werden und `__<variable_name>` erstellen!
```python
@dataclass
class Point:
_x: InitVar[float]
_y: InitVar[float]
def __post_init__(self, x, y):
self.__x = x
self.__y = y
```
---
## Private, Getter, Setter
- Geht auch ohne `InitVar`
- Keine Parameter für `__post_init__`
- Also auch keine Parameter beim erstellen
```python
@dataclass
class Point:
def __post_init__(self):
self.__x = 0
self.__y = 0
```
---
## Private, Getter, Setter
- `x` und `y` sind nicht mehr von außen sichtbar
```python
p = Point()
print(p.__x) # Error
```
- außer man erstellt einen *Getter* (`@property`)
```python
@dataclass
class Point:
# ...
@property
def x(self) -> float:
return self.__x
print(Point(3, 1).x) # Prints 3
```
---
## Private, Getter, Setter
Genauso kann man auch private Attribute setzbar machen:
```python
@dataclass
class Point:
# ...
@x.setter
def x(self, new_value: float):
self.__x = new_value
p = Point(3, 1)
print(p.x) # 3
p.x = 4
print(p.x) # 4
```
---
# Fragen?

BIN
Tutorium/tut11/slides.pdf Normal file

Binary file not shown.

View File

@ -0,0 +1,51 @@
from dataclasses import InitVar, dataclass
from typing import Any, Callable
def iterating_dict():
courses: dict[str, list[str]] = {
"eidp": ["np19", "az34", "jf334"],
"mathe": ["aw331", "pl67"],
"sdp": ["xy111", "xz112"],
}
for course, students in courses.items():
print(f"{courses}: {students}")
def mutable_keys():
lists_as_keys: dict[list[str], str] = {
["np19", "az34", "jf334"]: "eidp", # type: ignore
["aw331", "pl67"]: "mathe", # type: ignore
["xy111", "xz112"]: "sdp", # type: ignore
}
print(lists_as_keys)
def what_is_a_value():
ops: dict[str, Callable] = {
'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x / y,
}
can_execute = True
def function(x: int) -> int:
global can_execute
if can_execute:
can_execute = False
return x + 1
return x
def can_execute_again():
global can_execute
can_execute = True
@dataclass
class Point:
def __post_init__(self):
self.__x = 0
self.__y = 0
@property
def x(self) -> float:
return self.__x