marp, paginate, theme, footer, style
marp | paginate | theme | footer | style |
---|---|---|---|---|
true | true | rose-pine | EidP 2024 - Nils Pukropp - https://git.narl.io/nvrl/eidp-2024 | .columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; } |
Tutorium 12 - 2025-01-16
Datenklassen, Funktionale Programmierung, Blatt 12
Datenklassen - field(..)
- Wird benutzt um zusätziche Funktionalität in Datenklassen für Attribute zu ermöglichen:
init=..
: ob das Attribut als Parameter in der__init__
(Konstruktor) auftauchen solldefault=..
: ob das Attribut einen Standardwert bekommen soll (nur primitive Datentypen)default_factory=..
: Falls es ein Objekt ist brauchen wir eine Factory (z.B. Konstruktor) welche das Objekt erstelltrepr=..
: Ob das Attribut in der__repr__
auftauchen soll
Datenklassen - Beispiel field(..)
@dataclass
class Person:
__name: str = field(init=False, default="")
"""das Feld taucht nicht im Konstruktor auf und wird mit "" initialisiert
"""
__my_hobbies: list[str] = field(init=False, default_factory=list)
"""das Feld lässt sich nur mit einer Factory erstellen
"""
__secrets: list[str] = field(init=False, default_factory=list, repr=False)
"""das Feld taucht nicht in der `__repr__` Dunder-Methode auf
"""
me = Person()
print(me) # Person(_Person__name='', _Person__my_hobbies=[])
Datenklassen - InitVar
InitVar
ermöglicht einen Platzhalter zu erstellen- speichert nichts ab
- wird aber als Parameter im Konstruktor übergeben
@dataclass
class Time:
hours: InitVar[int]
minutes: InitVar[int]
__time: int = field(init=False)
my_time = Time(hours=4, minutes=20)
my_time = Time(4, 20)
Datenklassen - InitVar
und __post_init__
- Um
InitVar
zu verwenden brauchen wir die__post_init__
- die
__post_init__
bekommt dieInitVar
übergeben - dann können wir die eigentlichen Attribute initialisieren
- die
@dataclass
class Time:
hours: InitVar[int]
minutes: InitVar[int]
__time: int = field(init=False)
def __post_init__(self, hours: int, minutes: int):
self__time = hours * 60 + minutes
Was ist property
?
- Syntax-Sugar,
Funktionale Programmierung
Funktionale Programmierung - was ist das?
- Funktionen sind äquivalent zu Datenobjekten
- anonyme Funktionen aka Lambdas
- Closures
- Programmablauf mit Verkettung und Komposition von Funktionen
Funktionen sind Datenobjekte
- Jede Funktion hat den Datentyp
Callable
- Wir können Funktionen wie alle anderen Objekte variabeln zuweisen
def add(a: int, b: int) -> int:
return a + b
add_but_variable = add
print(add_but_variable(3, 2)) # 5
Anonyme Funktionen - lambda
- Mit dem
lambda
Keyword lassen sich anonyme Funktionen definieren ohnedef
- Bietet sich vor allem an für kleine Funktionen und Kompositionen von Funktionen
print(reduce(lambda x, y: x + y, [1, 2, 3, 4])) # 10
- hat als Datentyp auch
Callable
add: Callable[[int, int], int] = lambda x, y: x + y
Higher-Order Functions
- Eine Funktion muss eine der Eigenschaften haben:
- nimmt eine oder mehrere
Callable
als Argument - gibt ein
Callable
zurück
- nimmt eine oder mehrere
Higher-Order-Function - map
-
Wendet ein
Callable
auf jedes Element in einemIterable
andef map[T, R](func: Callable[[T], R], xs: Iterable[T]) -> Iterable[R]: return [func(x) for x in xs] numeric_list = list(map(lambda e: int(e), ['1', '2', '3'])) print(numeric_list) # [1, 2, 3]
Higher-Order-Function - filter
filter
verarbeitet Datenstrukturen anhand eines Prädikats (Callable
)- behält nur Elemente die das Prädikat erfüllen
def filter[T](predicate: Callable[[T], bool], xs: Iterable[T]) -> Iterable[T]: return [x for x in xs if predicate(x)] predicate: Callable[[int | None] bool] = lambda e: not e is None none_free_list: list[int] = list(filter(predicate, [1, 2, 3, None, 5, 6])) print(none_free_list) # [1, 2, 3, 5, 6] - kein None
Higher-Order-Function - fold
- Kombiniert Elemente einer Datenstruktur
def fold[T](func: Callable[[T, T], T], xs: Iterable[T]) -> T: it: Iterator[T] = iter(xs) value: T | None = None for x in it: match value: case None: value = x case _: value = func(value, x) if not value: raise TypeError("can't fold empty list") return value sum: Callable[[Iterable[int]], int] = lambda xs: fold(lambda x, y: x + y, xs) print(sum([1, 2, 3, 4])) # 10
keine Higher-Order-Function - flatten
- Nimmt mehrdimensionale Listen und macht eine Liste draus
def flatten(xs: Iterable[Any]) -> Iterable[Any]: new_list = [] for s in xs: if isinstance(s, Iterable): new_list += flatten(s) else: new_list.append(s) return new_list print(list(flatten([[1, 2, 3], 4, [[5, 6], 7, [8, 9]]]))) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
- nimmt weder
Callable
als Argumente - gibt kein
Callable
zurück - ist keine Higher-Order-Function
Closures
- Verkettete Funktionen, bei denen die Variabeln aus vorherigen benutzt werden können
def poly(x: float) -> Callable[[float, float], Callable[[float], float]]: return lambda a, b: lambda c: a * x ** 2 + b * x + c print(poly(3)(2, 3)(5)) # 2 * 3 ** 2 + 3 * 3 + 5 = 32
- kein wirklich schönes Beispiel, ein besseres ist
compose
für Kompositionen
Komposition
- Verketten von Funktionen
def compose[T](*funcs: Callable[[T], T]) -> Callable[[T], T]: return fold(lambda f, g: lambda n: f(g(n)), funcs) f: Callable[[int], int] = lambda n: n + 42 g: Callable[[int], int] = lambda n: n ** 2 h: Callable[[int], int] = lambda n: n - 3 print(compose(f, g, h)(0))