{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1ddc11f3-0a8a-47ee-b7b7-6dfd22ac40d9",
   "metadata": {
    "editable": true,
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-8c0ecbebdebcad39",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    },
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "# Zusatz Programmierübung: Python Tutorial\n",
    "\n",
    "<div style=\"display:flex;\">\n",
    "    <div style=\"text-align: left\">\n",
    "        Willkommen zum Zusatz der ersten Programmierübung Einführung in Python 3.\n",
    "    </div>\n",
    "    <img style=\"float: right; margin: 0px 15px 15px 0px\" src=\"https://www.python.org/static/img/python-logo-large.c36dccadd999.png?1576869008\" width=\"100\" />\n",
    "</div>\n",
    "\n",
    "Bei diesem Tutorial handelt es sich um Python Konzepte die nicht notwendig sind das Modul zu bestehen. Sie dürfen trotzdem alles hier gelernte gerne verwenden.\n",
    "\n",
    "Wenn Sie Fragen oder Verbesserungsvorschläge zum Inhalt oder Struktur der Notebooks haben, dann können sie eine E-Mail an Phil Keier ([p.keier@hbk-bs.de](mailto:p.keier@hbk-bs.de?subject=[SigSys]%20Feedback%20Programmierübung&amp)) oder Martin Le ([martin.le@tu-bs.de](mailto:martin.le@tu-bs.de?subject=[SigSys]%20Feedback%20Programmierübung&amp)) schreiben.\n",
    "\n",
    "Link zu einem Python Spickzettel: [hier](https://s3.amazonaws.com/assets.datacamp.com/blog_assets/PythonForDataScience.pdf)\n",
    "\n",
    "Der Großteil des Python-Tutorials stammt aus der Veranstaltung _Deep Learning Lab_ und von [www.python-kurs.eu](https://www.python-kurs.eu/python3_kurs.php) und wurde für _Signale und Systeme_, sowie _Einführung in die Programmierung für Nicht Informatiker_ angepasst."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f47e9f08-663b-4c2f-85c7-8b6c7f654b45",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-40ca946ad65b66b6",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Konventionen\n",
    "\n",
    "Python hat einen Grundlegenden Styleguide 2001 von Guido van Rossum festgelegt in [PEP 8](https://peps.python.org/pep-0008/).\n",
    "\n",
    "Dazu nur ein paar Anmerkungen:\n",
    "\n",
    "1. Variabel- & Funktionsnamen werden immer kleingeschrieben und mittels \"snake case\" geschrieben. Bsp.: `is_alive`\n",
    "2. Zum Einrücken sollten 4 Leerzeichen verwendet werden die mit Tab eingeleitet werden. (Jupyter macht dies Automatisch richtig)\n",
    "3. Beim schreiben von Kommentaren folgt nach `#` immer ein Leerzeichen. Bsp.: `# Kommentar`\n",
    "4. Importe aus Modulen sollen getrennt sein.\n",
    "\n",
    "   Richtig:\n",
    "   ```python\n",
    "   import os\n",
    "   import sys\n",
    "   ```\n",
    "   \n",
    "   Falsch:\n",
    "   ```python\n",
    "   import os, sys\n",
    "   ```\n",
    "\n",
    "5. Mehrfach importe aus einem Modul sind dennoch mit `,` gern gesehen.\n",
    "\n",
    "   Bsp.:\n",
    "   ```python\n",
    "   from dataclass import dataclass, field, asdict\n",
    "   ```\n",
    "\n",
    "6. Nach jedem `,`, `:`(außer beim slicing) und operator `+, =, etc.` folgt ein Leerzeichen.\n",
    "  \n",
    "   Bsp.:\n",
    "   ```python\n",
    "   x = 4 + 2\n",
    "   arr[5:10]\n",
    "   ```\n",
    "\n",
    "7. Kein unnötiges Ausrichten von Variablen.\n",
    "\n",
    "    So nicht:\n",
    "   ```python\n",
    "   x    = 4\n",
    "   y    = 5\n",
    "   name = \"Lisa\"\n",
    "   ```\n",
    "\n",
    "8. Die Variablnamen `l` (Kleingeschriebenes L), `O` (Großes o) und `I` (Großes i) sollten niemals als Einzelvariablennamen verwendet werden, da diese Schwer von `i` (Kleines i), `0`(Die Zahl Null) und `L` (Großes L) in einigen Schriftarten zu unterscheiden sind. (Sollte mit Jupyter kein Problem darstellen)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4abb5645-0172-4fd7-8f79-02af5140b234",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-ef37f36145723997",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Generatoren\n",
    "\n",
    "Nachdem wir bereits Funktionen kennengelernt haben, welche einen Rückgabewert haben, lernen wir nun ein Python Konzept kennen, dass \"On the fly\" Daten zur Verfügung stellt.\n",
    "\n",
    "Einen häufig verwendeter Generator, denn wir bereits kennengelernt haben, ist die `range` Funktion.\n",
    "\n",
    "Mit einem einfachen `print` lässt sich dies auch bestätigen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f8f4aee6-ce57-41f8-af18-7256b0126515",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-1e78bfa1751878c9",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "range(0, 10)\n"
     ]
    }
   ],
   "source": [
    "print(range(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc9f0f52-0a0d-4e5c-8b6f-d7a2b7c5b2e3",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-1c1a0f19a370c840",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Statt wie vielleicht zu erwarten die Liste von werten `0...9` als Ausgabe zu bekommen, gibt uns Python lediglich `range(0, 10)` zurück.\n",
    "\n",
    "Möchte man die Werte direkt evaluiert haben muss der `*`-Operator verwendet werden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "89986045-bb47-4bca-b561-bab9bb2b988b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-723d591c32fda1a7",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 1 2 3 4 5 6 7 8 9\n"
     ]
    }
   ],
   "source": [
    "print(*range(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2248b888-aa12-4367-b1f4-bb4befc43df4",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-19e02ebb63f72a88",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Dabei verändert `range(10)` die `print` Funktion indem es alle Generator Werte als Parameter einsetzt `print(0,1,2,3,4,5,6,7,8,9)` und danach aufruft.\n",
    "\n",
    "Um selber einen Generator zu definieren benötigt man das Python Keyword `yield`. Im Gegensatz zum Normalen `return` wird die Berechnung nur gestoppt und zu einem späteren Zeitpunkt ausgeführt. Sozusagen hat der Generator ein veränderbaren Zustand.\n",
    "\n",
    "Die Syntax hierzu ist im Allgemeinen:\n",
    "\n",
    "```python\n",
    "def <funktion-name>(<parameterliste>):\n",
    "    # do something\n",
    "    yield <ergebnis>\n",
    "```\n",
    "\n",
    "Eine rudimentäre Funktion `count_to` lässt sich dementsprechend wie folgt definieren:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "e4686bab-6686-4464-9015-54d420087e5a",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-795ddd785249ca9d",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "This is a Generator: <generator object count_to at 0x7fe8b55cfed0>\n",
      "This Generator evaluates to: 0 1 2 3 4 5 6 7 8 9\n"
     ]
    }
   ],
   "source": [
    "def count_to(n):\n",
    "    count = 0\n",
    "    while count < n:\n",
    "        yield count\n",
    "        count += 1\n",
    "\n",
    "print(\"This is a Generator:\", count_to(10))\n",
    "print(\"This Generator evaluates to:\", *count_to(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cbdb90b-bedf-4197-97b8-8baf58c92190",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-c2c489efaeeb75dd",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "**Aufgabe**: Schreibe einen Generator `square_count` mit einem Eingabeparameter `n`, welcher die Quadratzahlen von $0\\dots n^2$ ausgibt.\n",
    "\n",
    "Hinweis: Bei Eingabe von `5` soll die Ausgabe `0 1 4 9 16` sein."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "2df62e85-d5a5-4664-bdf6-c544ed8fb0d1",
   "metadata": {
    "editable": true,
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-a037e576943e230b",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    },
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "def square_count(n):\n",
    "    for i in range(n):\n",
    "        yield i*i\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "234d782c-88f4-4bc2-a872-15a592ee1248",
   "metadata": {
    "editable": true,
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-a6c43a5365ad736d",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    },
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Hier werden die Loesungen getestet...\n",
    "### BEGIN HIDDEN TEST\n",
    "for n in range(10):    \n",
    "    assert [i*i for i in range(n)] == [i for i in square_count(n)]\n",
    "### END HIDDEN TEST"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e1cb8b2-cdaa-422a-9889-c4e0794d7d81",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5f6f32fed19a1933",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Generatoren können auch eine unendliche Menge an Daten zurückgeben. Dieses Ziel kann man erreichen indem der Generator unendlich oft ausgeführt wird. Da die Daten zur Laufzeit berechnet werden kann man von einer unendlichen Menge sprechen.\n",
    "\n",
    "Um eine Berechnung nie enden zu lassen muss die Bedingung der Schleife immer `wahr` bleiben.\n",
    "Dies erreicht man durch die Syntax `while True:`, aber Python ist eben Python und die Syntax `while 1:` ist Laufzeit effizienter.\n",
    "\n",
    "Schauen wir uns nun das Beispiel eines unendlichen Generator an der Fortwährend die nächste Fakultät ausgibt:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "e7a2e825-b67d-4618-9358-4ee06ef5cc38",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-55717dfe7800e3fb",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "def faktoriel_gen():\n",
    "    curr = 1\n",
    "    count = 1\n",
    "    while 1:\n",
    "        curr = count * curr\n",
    "        count += 1\n",
    "        yield curr"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "496d59e9-e3c1-47d1-982d-8c5688cb7893",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-7d21c6426cee23f0",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Vorsicht (!!) wertet man diesen Generator nun aus würde die Berechnung niemals enden. Um den nächsten Wert der Berechnung zu erhalten hat Python die Funktion `next`, welche den nächsten Zustand des Generators ausgibt. Mit einem `for`-loop und `next` lassen sich dann die Fakultäten der Zahlen bis `5` einfach ausgeben:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "4249c820-f03c-4393-bb8f-5078f9a9a0b8",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-166ede3f392e88e7",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "6\n",
      "24\n",
      "120\n"
     ]
    }
   ],
   "source": [
    "gen = faktoriel_gen()\n",
    "\n",
    "for _ in range(5):\n",
    "    print(next(gen))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b0d160f-d445-4877-a634-2de69336e70a",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2e5778830ac74399",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Da der Zustand des Generator gespeichert ist lässt sich mit einem weiteren aufruf in nächster Zelle auf $6! = 720$ ausgeben:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "32e5eb93-507a-490e-9395-717bc7717973",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-30acb8e9a68a7794",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "720\n"
     ]
    }
   ],
   "source": [
    "print(next(gen))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9901cb74-faf9-47f1-b87e-60d9683778d3",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-3f6a4841c593371f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "**Aufgabe**: Schreibe einen Generator `naturals`, welcher mit jedem Aufruf die nächste natürliche Zahl ausgibt. (Angefangen mit 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "a8b2697c-706b-4744-a4b3-00682d6c95c3",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-09dd94e802cad9bc",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "def naturals():\n",
    "    curr = 1\n",
    "    while 1:\n",
    "        yield curr\n",
    "        curr += 1\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "403c70b9-3ac3-4a85-a302-d9c7d0585cbc",
   "metadata": {
    "editable": true,
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-e065f7326fb561ad",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    },
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Hier werden die Loesungen getestet...\n",
    "### BEGIN HIDDEN TEST\n",
    "import random\n",
    "test_n = random.randint(5, 17)\n",
    "test_gen = naturals()\n",
    "for i in range(1, test_n):\n",
    "    assert i == next(test_gen)\n",
    "### END HIDDEN TEST"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75a1949d-d0b6-4ea2-83c9-c5f8f15ae4f0",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-701ee003544a4616",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Lambda - Anonyme Funktionen\n",
    "\n",
    "Ein eher kontroverses Feature ist das Keyword `lambda`, welche es ermöglicht \"anonyme Funktionen\" oder \"oneliner\" zu schreiben.\n",
    "Eine `lambda` Funktion lässt sich auch einer Variablen zuweisen.\n",
    "\n",
    "Die Syntax hierfür:\n",
    "```python\n",
    "[<variable> =] lambda <eingabewerte>: <berechnung und gleichzeit Rückgabewert>\n",
    "```\n",
    "\n",
    "Möchte man beispielweise eine einfache Square Funktion schreiben, lässt sich dies wie bei der `List Comprehension` in einer Zeile erledigen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "7cae2e6e-47e8-46c5-97de-0d0da99ff03d",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-60308748fe5798d1",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number: 3 \n",
      "Squared Number: 9\n"
     ]
    }
   ],
   "source": [
    "square = lambda n: n*n\n",
    "print(\"Number:\", 3, \"\\nSquared Number:\", square(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60f06fff-1f23-441b-af0c-6cfbb1c862e6",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-04db14786b869da9",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Die Funktion ist deshalb anonym da sie keinen Namen hat wie folgender Output veranschaulicht:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "3b7acada-ed40-4a72-b40f-7609e4989ba9",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-f9dc9e005746fa88",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function <lambda> at 0x7fe8b56093a0>\n"
     ]
    }
   ],
   "source": [
    "print(square)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f13c0d4-bd7c-42d8-b387-40d994348715",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-ba511670b3205191",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Probieren Sie es gerne selber aus.\n",
    "\n",
    "**Aufgabe**: Schreibe eine `lambda`-Funktion die den Input auf die Funktion $f(x) = x^4+2$ abbildet, und weisen Sie dieser Funktion die variable `f` zu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "e5975460-4d13-4a7a-ab4c-e5a637ff11ee",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-b6782b076b8e1363",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "f = lambda n: n**4+2\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "dcc8cad0-8582-471e-bccc-ea4516ee1121",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-837edeb5c7f4b83e",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden die Loesungen getestet...\n",
    "### BEGIN HIDDEN TEST\n",
    "import random\n",
    "n = random.randint(2,90)\n",
    "assert n**4+2 == f(n)\n",
    "### END HIDDEN TEST"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41bb0337-7194-4482-a5b9-af1243d4809c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-bbe0bad0c47d41ae",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Type Hints\n",
    "\n",
    "Mit [PEP 484](https://peps.python.org/pep-0484/) wurden in Python die `type hints` eingeführt. Die Motivation dafür war es eine Standard Syntax zu definieren, welche einem die Möglichkeit gibt den In- und Output von Funktionen besser zu bestimmen. Unteranderem verbessert sich die Testbarkeit von Python Programmen ungemeinen wenn Type Hints vorhanden sind.\n",
    "\n",
    "(!!!) Type hints können nicht für `lambda`-Funktionen genutzt werden!\n",
    "\n",
    "Die Allgemeine Syntax dafür:\n",
    "```python\n",
    "def <funktions-name>(<parameter1>: <type>, <parameter2>: <type>) -> <output-type>:\n",
    "    # do something\n",
    "    return <value of definied output-type>\n",
    "```\n",
    "\n",
    "Python ist eine dynamische typisierte Sprache. Das heißt, dass der Typ einer Variable immer wieder überprüft werden muss. So kann eine Ganzzahl `int` mit einer einfachen addition in eine Flieskommazahl `float` überführt werden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "d2148990-b5cb-4fe5-8751-bac8338fa581",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-560aa9d85c5a4383",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3 <class 'int'>\n",
      "3.14 <class 'float'>\n"
     ]
    }
   ],
   "source": [
    "number = 3\n",
    "print(number, type(number))\n",
    "number += 0.14\n",
    "print(number, type(number))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56c143f5-1c67-4999-8656-dc68d5912fb4",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-a85903e6bffa1ff1",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Um diese unerwünschten Typen wechsel zu vermeiden, kann man type hints verwenden. Type hints sind nur eine Info keine Garantie das der Typ einer Variable sich ändert!\n",
    "\n",
    "Eine nutzlose Funktion die den Eingabewert als `int` in einen `str` umwandelt sieht wie folgt aus:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "642dc60b-fa8d-4655-bc47-815f39365506",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-94708ecde4287ff7",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number 42\n"
     ]
    }
   ],
   "source": [
    "def useless(number: int) -> str:\n",
    "    return \"Number {}\".format(number)\n",
    "\n",
    "print(useless(42))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccd50282-1132-40b2-8eea-b6e6e5e0e943",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-220685a52d6807ca",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Decoraters\n",
    "\n",
    "Das Konzept der Dekoratoren lässt sich am einfachsten mit \"Funktionen die als Eingabeparameter Funktionen haben\" erklären.\n",
    "\n",
    "Die Syntax hierfür ist die selber wie für Funktionen, nur das als Eingabe eine Funktion erwartet wird:\n",
    "```python\n",
    "def <decorater-name>(<parameter>):\n",
    "    # do something\n",
    "```\n",
    "\n",
    "Dabei gibt es zwei möglichkeiten Dekoratoren zu nutzen.\n",
    "\n",
    "Methode 1 - Funktion ruft Funktion auf:\n",
    "```python\n",
    "<decorater>(<funktion>)\n",
    "```\n",
    "\n",
    "Methode 2 - Keyword `@`:\n",
    "```python\n",
    "@<decorator>[<parameter>]\n",
    "def <function>(<parameter>):\n",
    "    # do something\n",
    "```\n",
    "\n",
    "Schauen wir uns dazu ein Beispiel an. Der Debbug Decorator soll den Namen der aufgerufenen Funktion mittels `print` ausgeben und dann die Funktion (hier `f`) zurückgeben:\n",
    "\n",
    "Hinweis: Python speichert den namen einer Funktion in der versteckten (magischen) Variable `__name__` ab."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "08b50a8b-f410-4759-809c-e0301b2889b4",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-603f42aad9017a1e",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "square "
     ]
    },
    {
     "data": {
      "text/plain": [
       "16"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def debug(f):\n",
    "    print(f.__name__, end=\" \")\n",
    "    return f\n",
    "\n",
    "@debug\n",
    "def square(n):\n",
    "    return n*n\n",
    "\n",
    "square(4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "777904ec-3567-41c8-a619-2c38ad6b6c51",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-ea3aeed85ff0587c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Dataclasses \n",
    "\n",
    "Allgemein auf Klassen wird hier nicht eingegangen jedoch ein Konzept, welches mit [PEP 557](https://peps.python.org/pep-0557/) eingeführt wurde, soll folgend stärker beleuchtet werden.\n",
    "\n",
    "Datenstruckturen wie `dict`, `set`, `list`, etc. sind mächtige Werkzeuge und ermöglichen dem Programmierer Daten in vielen Formen akkurat dazustellen. Möchte man jedoch feste Datenstruckturen mit genau definierten Werten verwenden eignet sich das Modul `dataclasses`.\n",
    "\n",
    "Dazu wird eine Klasse mit dem Keyword `class` definiert und dem Decorator `dataclasses.dataclass` ausgesattet. Folglich können Feste Datenobjekte mit definierter Strucktur erstellt werden.\n",
    "\n",
    "Zunächst wird das Modul aus der Standard Bibliothek importiert:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "24392aea-8ba8-4f9e-ba95-5bf5c99a127b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-fa6710d8dd259c2d",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "from dataclasses import dataclass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abb46684-015f-48c7-98b8-f2955e71de2d",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-36842ee26321c3ec",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Danach kann eine Klasse erstellt werden. Erstellen wir zunächst eine Klasse `Person`, welche die Werte `vorname` und `nachname` als Strings bereitstellen soll:\n",
    "\n",
    "Wichtig: Python Klassen fangen immer mit einem Großbuchstaben an. Mit ausnahme der Standard Bibliothek. Die `range` Funktion lässt sich zwar verwenden wie eine Funktion, ist aber eigentlich eine Klasse!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "b70f39ca-6e57-4b03-a3be-c3b5ac604a9c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-8b8b2e316c36725c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class Person:\n",
    "    vorname: str\n",
    "    nachname: str"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94901847-a849-463b-968a-b498a9f84edc",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-846dcecf70fd8d14",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Möchten wir nun eine Person erstellen sieht dies wie folgt aus:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "27320011-ee5d-43c9-8e07-fbd3036680f7",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-1cf940f3240bf428",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Person(vorname='Eduard', nachname='Jorswieck') <class '__main__.Person'>\n"
     ]
    }
   ],
   "source": [
    "person = Person(\"Eduard\", \"Jorswieck\")\n",
    "print(person, type(person))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be0c2b13-937e-4073-8dfa-a6d305ed9c6f",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-8338f70364284fc7",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Wie dem Output zu entnehmen ist die Variable `person` ein Objekt vom Typ `Person` und hält die Werte `vorname='Eduard'` und `nachname='Jorswieck'` vor.\n",
    "\n",
    "Auf die einzelnen Werte innerhalb der Dataclass kann nun per `.` Operator zugegriffen werden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "191bb87c-06ab-40a4-b5b3-56e9c533b930",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-b6da422f93431723",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vorname: Eduard\n",
      "Nachname: Jorswieck\n"
     ]
    }
   ],
   "source": [
    "print(\"Vorname:\", person.vorname)\n",
    "print(\"Nachname:\", person.nachname)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e85dee5-09ea-4c00-9730-05fb006ced2b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-d80fa3ef35e81dab",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Dataclasses bieten auch den Vorteil, dass ihre Werte direkt über die Variablennamen definiert werden können. Dabei spielt die Reihenfolge dann keine Rolle mehr."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "a5ea4ef9-dcba-4411-a2e8-1a1a811e6cac",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-20f0d6f2cb9ef5df",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Person(vorname='Martin', nachname='Le') <class '__main__.Person'>\n"
     ]
    }
   ],
   "source": [
    "person2 = Person(nachname=\"Le\", vorname=\"Martin\")\n",
    "print(person2, type(person2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbc9c477-903b-4673-aab0-c8fe2b29a9bd",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-9f14c6d500ad6d08",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "**Aufgabe**: Erstelle eine Dataclass `Auto` welche die Attribute `beschleunigung` als `int` und `marke` als `str` beinhält."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "92173deb-dff1-4853-a01e-4e633f3c3aee",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-571687935c72333a",
     "locked": false,
     "points": 1,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "@dataclass\n",
    "class Auto:\n",
    "    marke: str\n",
    "    beschleunigung: int\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68db3f9c-b6ad-4454-87be-71a78a209a40",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2489e8269aa6414d",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Nicht immer sind alle Werte vorhanden und damit dies nicht zum Problem wird können Standardwerte vergeben werden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "ce05d4eb-747b-41ad-9fea-5430b8310e8c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-92f8a3cacfda2c15",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class Person:\n",
    "    vorname: str = \"Max\"\n",
    "    nachname: str = \"Mustermann\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4def98c-60a9-4dcf-a71b-6be49f853b3b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-009056608941f805",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Wird nun eine Dataclass ohne Eingabeparameter erstellt, werden ihr ihre Standardwerte zugewiesen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "6d5e0c5d-0f70-4303-83cb-234e9523500c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-dbc29451821056d9",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Aufruf mit print: Person(vorname='Max', nachname='Mustermann')\n"
     ]
    }
   ],
   "source": [
    "nameless_person = Person()\n",
    "print(\"Aufruf mit print:\", nameless_person) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0f08b60-5f24-4325-89a4-896d73834515",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-9f1b7aa9c4607b43",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Die korrekte Behandlung von nicht vorhandenen Werten innerhalb Dataclasses, kann erreicht werden durch die Zuhilfename der Funktion `field` aus dem `dataclasses` Modul.\n",
    "\n",
    "Dazu wird diese Importiert, und als Standardwert für die Variable zugewiesen. Beim aufrufen von `field` muss dabei eine `default_factory` angegeben werden. Dies ist eine Funktion die einen Standardwert zurückgibt. Meist wird hierbei für String Typen die `str`-Funktion verwendet, etc.."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "0f1f5a8b-8c2b-42a7-a2be-771216552066",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-f1cdd80f03fb05f4",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Person(vorname='', nachname='')\n"
     ]
    }
   ],
   "source": [
    "from dataclasses import field\n",
    "\n",
    "@dataclass\n",
    "class Person:\n",
    "    vorname: str = field(default_factory=str)\n",
    "    nachname: str = field(default_factory=str)\n",
    "\n",
    "print(Person())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccee5184-2f5e-4b90-99ae-2702405c0ec9",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-1c171ff9c43179be",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "**Aufgabe**: Schreiben Sie eine Dataclass `Auto` mit den Werten `marke` als `str` und `beschleunigung` als `int`. Nutzen Sie für die Standardzuweisung die `field` Funktion aus dem Modul `dataclasses` mit ihren jeweiligen Typen als `default_factory`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "d17b07db-7340-47c0-ab1d-38d75b3194c0",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-ac7d4ba80c4a0341",
     "locked": false,
     "points": 1,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "@dataclass\n",
    "class Auto:\n",
    "    marke: str = field(default_factory=str)\n",
    "    beschleunigung: int = field(default_factory=int)\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2a16cf4-a0b8-4bcd-8fa4-4143be3ca566",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-f17e7fb6600c6c87",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Dataclasses haben den Vorteil, dass sie sich leicht umwandeln lassen. Dazu bietet das Modul `dataclasses` einige Funktionen an zum Umwandeln einer Dataclass in ein `tuple` oder `dict`. Schauen wir uns im Folgenden an wie die Klasse Person in ein `dict` mit der Funktion `asdict` aus dem Modul `dataclasses` umgwandelt wird. Für mehr Funktionen und Möglichkeiten schauen Sie gerne in die Dokumentation zu [Dataclasses](https://docs.python.org/3.11/library/dataclasses.html)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "369599ab-87f1-4849-abfd-6af86d160975",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-899936f2048aa8bc",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataclass: Person(vorname='Ulrich', nachname='Reimers')\n",
      "Als Dictionary: {'vorname': 'Ulrich', 'nachname': 'Reimers'}\n"
     ]
    }
   ],
   "source": [
    "from dataclasses import asdict\n",
    "\n",
    "person3 = Person(vorname=\"Ulrich\", nachname=\"Reimers\")\n",
    "print(\"Dataclass:\", person3)\n",
    "print(\"Als Dictionary:\", asdict(person3))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35cf1031-9344-4cff-a13a-7674fceb4b4b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-64f568f2137fb4d0",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Structural Pattern Matching\n",
    "\n",
    "Eines der neueren Konzepte die mit Python 3.10 eingeführt wurde ist das Keyword `match` und das dem dazugehörige \"Structurals Pattern Matching\", wie in [PEP 622](https://peps.python.org/pep-0622/) beschrieben. Jedoch ist es nur teils vergleichbar mit dem aus anderen Programmiersprachen bekannten Konzept des `switch-case` (Bsp. C: [Switch Case in C](https://www.programiz.com/c-programming/c-switch-case-statement)). In C ersetzt `switch` das sukzessive Verwenden von `if-else` Anweisungen. In Python hingegen können Objekteigenschaften direkt geprüft werden, schauen sie sich dazu gerne das PEP 622 an.\n",
    "\n",
    "Im folgenden soll gezeigt werden, wie `if-else` Anweisungen mit `match` ersetzt werden können.\n",
    "\n",
    "Nutzen wir dafür das Beispiel der [HTTP Status Codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) mit denen Webserver mit Browsern kommunizieren. Der Bekannteste Status Code in der allgemeinen Bevölkerung ist `404 - Not Found`, welcher zurückgegeben wird wenn eine Resource im Internet (auf einem Server) nicht gefunden wurde.\n",
    "\n",
    "Die folgende Funktion soll einen kleinen Teil der Client Error Status Codes `4XX` für uns übersetzen.\n",
    "\n",
    "| Code | Explanation  |\n",
    "|------|--------------|\n",
    "| 400  | Bad Request  |\n",
    "| 401  | Unauthorized |\n",
    "| 403  | Forbidden    |\n",
    "| 404  | Not Found    |\n",
    "\n",
    "\n",
    "Klassisch und vollkommen richtig würde die Funktion wie folgt aufgebaut sein:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "16e46879-62fe-426b-954b-902cc5049aed",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-306c298b8e352c32",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Not Found'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def http_400_errors(status: int) -> str:\n",
    "    if status == 400:\n",
    "        return \"Bad Request\"\n",
    "    elif status == 401:\n",
    "        return \"Unauthorized\"\n",
    "    elif status == 403:\n",
    "        return \"Forbidden\"\n",
    "    elif status == 404:\n",
    "        return \"Not Found\"\n",
    "    else:\n",
    "        return \"Unknown Status\"\n",
    "\n",
    "http_400_errors(404)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09efac1e-f92a-4250-ac47-818284fbdca0",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-6c2e92150acdc4c5",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Mit dem `match` Statement spart man sich alle Ausdrücke mit der Strucktur `status ==`, der `else`-case würd in `match`-Statements mit `_` abgefangen.\n",
    "\n",
    "Damit hat selbige Funktion folgerichtig die Strucktur:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a6a89513-23bd-43b5-9f2d-869bac06150a",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-cef2c7cb5bce1a40",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Forbidden'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def http_400_errors(status: int) -> str:\n",
    "    match status:\n",
    "        case 400:\n",
    "            return \"Bad Request\"\n",
    "        case 401:\n",
    "            return \"Unauthorized\"\n",
    "        case 403:\n",
    "            return \"Forbidden\"\n",
    "        case 404:\n",
    "            return \"Not Found\"\n",
    "        case _:\n",
    "            return \"Unknown Status\"\n",
    "\n",
    "http_400_errors(403)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75e2e2bc-5653-4cd5-b8c8-0b606cbcbbcc",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-7381271e6bcda170",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Und bei unbekanntem Status Code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b275aa3a-72eb-4725-b6ea-395b82f718cf",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-9f9a173d4193c09c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Unknown Status'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "http_400_errors(500)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5faafbdb-ec5c-4f4a-aa89-03dccb6a9c1d",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2b5a2381a1507c55",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Probieren Sie es selber aus.\n",
    "\n",
    "**Aufgabe**: Schreiben Sie eine Funktion `http_500_errors`, welche die Korresponierende Tabelle der HTTP `5XX - Server Errors` korrekt abbildet. Achten Sie darauf unbekannte Status Codes abzufangen.\n",
    "\n",
    "| Code | Explanation                     |\n",
    "|------|---------------------------------|\n",
    "| 500  | Internal Server Error           |\n",
    "| 501  | Not Implemented                 |\n",
    "| 502  | Bad Gateway                     |\n",
    "| 503  | Service Unavailable             |\n",
    "| 504  | Gateway Timeout                 |\n",
    "| 511  | Network Authentication Required |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f339464a-bdde-4103-ac38-27ec6f2190b1",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-02286e2e124c39a9",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# BEGIN SOLUTION\n",
    "def http_500_errors(status: int) -> str:\n",
    "    match status:\n",
    "        case 500:\n",
    "            return \"Internal Server Error\"\n",
    "        case 501:\n",
    "            return \"Not Implemented\"\n",
    "        case 502:\n",
    "            return \"Bad Gateway\"\n",
    "        case 503:\n",
    "            return \"Service Unavailable\"\n",
    "        case 504:\n",
    "            return \"Gateway Timeout\"\n",
    "        case 511:\n",
    "            return \"Network Authentication Required\"\n",
    "        case _:\n",
    "            return \"Unknown Status\"\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7e74177e-e073-43ad-99a0-bb2ee56263fe",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-f6427af9d56311e2",
     "locked": true,
     "points": 1,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden die Loesungen getestet...\n",
    "### BEGIN HIDDEN TEST\n",
    "codes = [500, 501, 502, 503, 504, 511]\n",
    "table = {\n",
    "    500: \"Internal Server Error\",\n",
    "    501: \"Not Implemented\",\n",
    "    502: \"Bad Gateway\",\n",
    "    503: \"Service Unavailable\",\n",
    "    504: \"Gateway Timeout\",\n",
    "    511: \"Network Authentication Required\",\n",
    "        }\n",
    "for status in codes:\n",
    "    assert table[status] == http_500_errors(status)\n",
    "### END HIDDEN TEST"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afe6543a-b41b-4d3c-9929-525a0463c6af",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-a65f2c871406072c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Walrus Operator - Assingment Expressions\n",
    "\n",
    "Der Grund warum Guido van Rossum das Python Projekt verlassen hat, ist der Walrus Operator `:=`, zu finden unter [PEP 572](https://peps.python.org/pep-0572/). \n",
    "\n",
    "Das Offizielle Statement:\n",
    "\n",
    "> \"The straw that broke the camel's back was a very contentious Python enhancement proposal, where after I had accepted it, people went to social media like Twitter and said things that really hurt me personally. And some of the people who said hurtful things were actually core Python developers, so I felt that I didn't quite have the trust of the Python core developer team anymore.\"\n",
    "> - Guido van Rossum\n",
    "\n",
    "Das Problem der Operator `:=` fügt keinerlei neue Funktionalität hinzu und erlaubt einzig und allein eine Zuweisung während einer Auswertung zu erlauben.\n",
    "\n",
    "Daher ein paar kurze Beispiele, lesen Sie ansonsten gerne PEP 572.\n",
    "\n",
    "Zuweisung mittels Walrus Operator: (Machen Sie das bitte nicht nach, Niemand wirklich Niemand möchte das sehen!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "8055d0b2-eb9f-4a1a-ac74-4bfcd6cbee81",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-860f5501722f78aa",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "# Normale Zuweisung\n",
    "walrus = True\n",
    "print(walrus)\n",
    "\n",
    "# Walrus Zuweisung\n",
    "(walrus := False)\n",
    "print(walrus)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af5f2aab-0e6d-41d8-b3e1-96969a5ad264",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2fb74418e2478ef5",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Berechnung und Zuweisung in einer Zeile.\n",
    "\n",
    "Walrus soll verwendet werden, wenn man vermeiden möchte das eine Berechnung zweimal ausgeführt wird.\n",
    "\n",
    "Beispiel Klassisch `n+1` wird zweimal berechnet:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b41cae11-20f2-4500-b32a-77775502f6ea",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-85ffa763f1c26ced",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    "n = 4\n",
    "if (n + 1) > 3:\n",
    "    print(n+1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ad0b0b7-5d85-4a1d-9083-30790c494035",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-da3163fec3bf5f09",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Mit Walrus lässt sich im `if` die Berechnung `n+1` der Variablen `out` zuweisen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "cde3f827-477c-46d4-8057-af35b694ebef",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-664ab7388a3cb90c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    "n = 4\n",
    "if (out := n + 1) > 3:\n",
    "    print(out)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16375c7c-ef2e-44b7-a5f1-2156248f616c",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-11c8ad1c8ab2ef15",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Ohne Walrus lässt sich dennoch immer vermeiden die Berechnung `n+1` zweimal auszuführen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "c1bf6d7e-8ce8-4770-8060-a6d18ab8d85e",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-2dbb33acbcf4f8a4",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    "n = 4\n",
    "out = n + 1\n",
    "if out > 3:\n",
    "    print(out)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "302a6dfd-2e3d-401d-93c9-1335e081e837",
   "metadata": {
    "editable": true,
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-3254f96c16f5c246",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    },
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "**Persönliche Meinung**: Ich rate davon ab Walrus `:=` zu verwenden. In meinen Augen macht es den Code Grundsätzlich unlesbar und spart im besten Fall 2-3 Zeilen Code. In meiner eigenen Programmiererfahrung gab es nie einen Grund den Operator zu verwenden, er fügte nie einen realen Nutzen in irgendeine meiner Berechnungen ein. Dennoch wollt ich ihnen einmal Demonstrieren wie Walrus funktioniert."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
