JavaScript promise rejection: Loading CSS chunk katex failed. (error: https://git.narl.io/assets/css/katex.faca27c4.css). Open browser console to see more details.
Files
.vscode
Tutorium
tut01
tut02
tut03
tut04
tut05
tut06
tut07
tut08
tut09
tut10
tut11
tut12
src
README.md
notes.md
slides.pdf
tut13
tut14
src
.gitignore
LICENSE
README.md
eidp-2023/Tutorium/tut12
2024-01-26 01:02:16 +01:00
..
2024-01-19 05:29:56 +01:00
2024-01-18 13:33:28 +01:00
2024-01-26 01:02:16 +01:00
2024-01-19 05:29:56 +01:00

marp, paginate, theme, footer, header, math
marp paginate theme footer header math
true true rose-pine Tutorium 13 - 26.01.2024 - Nils Pukropp - https://s.narl.io/s/tutorium-13 mathjax

Tutorium 13 - 26.01.2024

Orga - Wiederholung Types - Functions! - Decorator


Orga


Wegen fehlendem Tutorium am 19.01.

  • Jeder kriegt die 6 Punkte für Anwesenheit
    • Auf Blatt 13 als Extrapunkte unter Anmerkungen
  • Sorry fürs nicht beantworten von manchen Nachrichten
  • Falls ihr glaubt ihr bekommt knapp nicht genug Punkte schreibt mich einfach an, man wird schon noch irgendwo Punkte finden

Syntax-Fehler

  • Für Syntax-Fehler habe ich im allgemeinen 0 Punkte in der jeweiligen Datei vergeben
    • Das euer Programm ausführbar ist sollte das mindeste sein!
    • Ihr sollt euer Programm sowieso selbständig testen und ich geh mal davon aus das ist nicht passiert wenn sich die Datei nichtmal ausführen lässt
  • Zeitdruck kann ich voll nachvollziehen

Nachträgliches ausbessern

  • Ihr verbessert euren SyntaxFehler (eure Python-Datei ist ausführbar)
  • Ihr schickt mir eine .zip oder eine .tar.gz mit dem verbesserten Code an nils@narl.io
    • verbessert nichts anderes!
    • Schreibt kurz in die Mail welches Blatt + Aufgabe + Kürzel
  • Ich korrigiere eure Abgabe nachträglich und ihr bekommt zumindest mehr als 0 Punkte
  • Bitte nur wenn ihr wirklich die Punkte braucht und habt etwas Geduld mit der Korrektur

Allgemeines

  • biete euch Übungen passend zur Klausur
    • kein genaues Datum, aber vor dem 09.02
  • Klausur ist wahrscheinlich am 19.02.
  • Short-Link zu der Übung https://s.narl.io/s/eidp-ub
    • aktuell noch nicht online

Link: https://s.narl.io/s/eidp-ub


Type annotations

(Wiederholung)


Type annotations - Was ist das?


Type annotations - Was ist das?

  • Jedes Objekt lässt sich mindestens einem Typ zuordnen
    • Objekte im mathematischen Sinne wie z.B. Variablen, Funktionen, ...
  • Dieser schränkt den Wertebereich ein
    • z.B. ist eine Variable x von Typ int eine Ganzzahl
    • ähnlich zur mathematischen Schreibweise x \in \mathbb{Z}
  • In der Informatik nennt man das Typisierung
    • Es gibt verschiedene Arten der Typisierung

Type annotations - Typisierung

  • dynamische Typisierung überprüft die gegebenen Typen zur Laufzeit
    • also erst wenn das Programm läuft
  • statische Typisierung überprüft die gegebenen Typen zur Übersetzungszeit
    • also während wir den Quellcode übersetzen

Type annotations - Typisierung

  • dynamische Typisierung überprüft die gegebenen Typen zur Laufzeit
    • also erst wenn das Programm läuft
  • statische Typisierung überprüft die gegebenen Typen zur Übersetzungszeit
    • also während wir den Quellcode übersetzen

Was ist nun Python?


Was ist nun Python?

  • dynamisch typisiert
    • wir müssen unsere .py Datei ausführen bevor wir wissen ob alles korrekt ist
  • Pylance ist ein eigenes Programm
    • es soll beim Schreiben bereits Typverletzungen erkennen
    • unvollständige Typüberprüfung, es soll nur den Entwickler unterstützen

Variabeln Typannotieren

  • variable_name: <Type> = ...
  • Beispiele:
    x: int = 3
    y: int = 5
    string: str = "Hello World!"
    
    # aber auch eigene Objekte (OOP)
    point: Point = Point(3, 1)
    
  • diese Annotation ist für uns optional

Funktionen Typannotieren

  • def func_name(param1: <Type>, param2: <Type>, ...) -> <Type>
  • Beispiele:
    def add(x: int, y: int) -> int:
        return x + y
    
    def div(x: float, y: float) -> Optional[float]:
        if y == 0.0:
            return None
        return x / y
    
  • diese Annotation ist verpflichtend und muss so vollständig wie möglich sein

Klassen Typannotieren

  • class ClassName:
        attribute_name1: <Type>
        attribute_name2: <Type>
        ...
    
  • Beispiel:
    @dataclass
    class Point:
        x: int
        y: int
    
  • diese Annotation ist verpflichtend und muss so vollständig wie möglich sein

Methoden Typannotieren

  • def method_name(self, param1: <Type>, ...) -> <Type>
  • Beispiel:
    class Point:
        x: int
        y: int
    
        def distance_from(self, other: 'Point') -> float:
            return math.sqrt((other.x - self.x) ** 2 + (other.y - self.y) ** 2)
    
  • self muss nicht Typannotiert werden, kann aber
  • other hingegen schon, wegen Python muss in der Klasse mit ' annotiert werden
  • diese Annotation ist verpflichtend

Datentypen von Datentypen

  • Manche Datentypen bauen sich aus anderen Datentypen auf
  • z.B. list ist eine Liste von Elementen mit einem Typ
  • hierfür verwenden wir [] um den Datentyp in list zu annotieren
    def sum(xs: list[int]) -> int:
        total: int = 0
        for x in xs:
            total += x
        return total
    
  • hierbei ist es wichtig so genau wie möglich zu annotieren!
  • diese Annotation ist verpflichtend

Häufige Fehler mit verschachtelten Typen


Fehlerquelle - tuple[...]

  • Tuple haben eine feste größe
  • Tuple sind endlich
  • Tuple können Elemente mit unterschiedlichen Typen haben
  • Die Datentypen der Elemente werden mit einem , in [] getrennt
  • Beispiel:
    tup: tuple[int, int, float, str] = (1, 2, 3.0, "hello world")
    
  • Diese Annotation ist verpflichtend

Fehlerquelle - dict[...]

  • Dictionary haben genau zwei zu definierende Typen
    • Key
    • Value
  • Beispiel:
    number_dictionary: dict[int, str] = {
        0: "zero",
        1: "one",
        2: "two",
    }
    
  • Diese Annotation ist verpflichtend
  • Diese kann weiter geschachtelt werden durch z.B. list als Value:
    • dict[int, list[str]]

Fehlerquelle - Typvariabeln (generische Typen)

  • manchmal wollen wir nicht genau wissen welchen Datentypen wir haben
  • dieser wird dann implizit von Python erkannt
  • wir stellen damit sicher dass eine Typvariable beliebig aber fest ist
  • Beispiel:
    def add[T](x: T, y: T) -> T:
        return x + y
    
  • T kann nur ein Datentyp sein, also muss type(x) == type(y) gelten
  • außer wir schrenken T mit | ein: T: (int | str) damit müssen x und y nicht den gleichen Datentypen haben
  • T lässt sich weiter einschränken durch T: (int, str), hierbei ist T entweder ein int oder (exklusiv) str

Fehlerquelle - Was ist TypeVar?

  • TypeVar ist aus früheren Python-Versionen
  • Typvariablen wurden vor der Python 3.12 so definiert:
    T = TypeVar('T')
    
  • sieht dumm aus, ist es auch, benutzt es nicht!

Fragen zu Typannotationen?


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 ohne def
  • 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
    

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))
    

Higher-Order Functions

  • nehmen eine oder mehrere Callable als Argument
  • geben ein Callable zurück

Higher-Order-Function - map

  • Wendet ein Callable auf jedes Element in einem Iterable an

    def 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: e is not 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
    
    flattened = list(flatten([[1, 2, 3], 4, [[5, 6], 7, [8, 9]]]))
    print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
  • nimmt weder Callable als Argumente
  • gibt kein Callable zurück
  • ist keine Higher-Order-Function

Fragen zur funktionalen Programmierung?


Weitere allgemeine Fragen?