diff --git a/Tutorium/tut11/README.md b/Tutorium/tut11/README.md index b0ef6f8..612a4cd 100644 --- a/Tutorium/tut11/README.md +++ b/Tutorium/tut11/README.md @@ -3,13 +3,13 @@ marp: true paginate: true class: invert # 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: --- -# 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 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 -dictionary = { - : , - : , - ... - : +courses: dict[str, list[str]] = { + "eidp": ["np19", "az34", "jf334"], + "mathe": ["aw331", "pl67"], + "sdp": ["xy111", "xz112"], } ``` -#### 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 -courses = { - "eidp": ["np163", "az34", "jf334"], - "mathe": ["aw331", "pl67"], - "sdp": [] +print(courses["eidp"]) # ["np19", "az34", "jf334"] + +courses["eidp"] += ["jk331"] + +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, } -``` \ No newline at end of file + +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 `_` benannt werden +- `__post_init__(self, )` muss definiert werden und `__` 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? \ No newline at end of file diff --git a/Tutorium/tut11/slides.pdf b/Tutorium/tut11/slides.pdf new file mode 100644 index 0000000..4915751 Binary files /dev/null and b/Tutorium/tut11/slides.pdf differ diff --git a/Tutorium/tut11/src/dictionary.py b/Tutorium/tut11/src/dictionary.py new file mode 100644 index 0000000..14dd49b --- /dev/null +++ b/Tutorium/tut11/src/dictionary.py @@ -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 \ No newline at end of file