1793 lines
46 KiB
Plaintext
1793 lines
46 KiB
Plaintext
{
|
||
"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": [
|
||
"# 3. Programmierübung: Python Tutorial - Extended Applications\n",
|
||
"\n",
|
||
"<div style=\"display:flex;\">\n",
|
||
" <div style=\"text-align: left\">\n",
|
||
" Willkommen zur dritten 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",
|
||
"In dieser Übung werden erweiterte Konzepte gelernt, welche das Konzipieren von Programmen stark vereinfacht.\n",
|
||
"\n",
|
||
"Wenn du Fragen oder Verbesserungsvorschläge zum Inhalt oder Struktur der Notebooks hast, dann kannst du mir gerne eine E-Mail an Phil Keier ([p.keier@hbk-bs.de](mailto:p.keier@hbk-bs.de?subject=[SigSys]%20Feedback%20Programmierübung&)) 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": [
|
||
"Python hat seit 2001 einen grundlegenden Styleguide, festgelegt von Guido van Rossum: [PEP 8](https://peps.python.org/pep-0008/).\n",
|
||
"\n",
|
||
"Hier ein paar wichtige Hinweise dazu:\n",
|
||
"\n",
|
||
"1. Variablen- und Funktionsnamen werden immer kleingeschrieben und im klassischen *snake_case* notiert. \n",
|
||
" Beispiel: `is_alive`\n",
|
||
"\n",
|
||
"2. Für Einrückungen nutzt man 4 Leerzeichen, üblicherweise automatisch per Tab gesetzt (Jupyter macht das ohnehin richtig).\n",
|
||
"\n",
|
||
"3. Kommentare starten nach dem `#` immer mit einem Leerzeichen. \n",
|
||
" Beispiel: `# Klarer Kommentar`\n",
|
||
"\n",
|
||
"4. Importe sollten sauber getrennt werden.\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. Mehrfachimporte aus einem Modul sind dagegen völlig in Ordnung:\n",
|
||
"\n",
|
||
"6. Nach `,`, `:` (außer beim Slicing) sowie Operatoren wie `+` oder `=` gehört ein Leerzeichen.\n",
|
||
"Beispiel:\n",
|
||
"\n",
|
||
" ```python\n",
|
||
" x = 4 + 2\n",
|
||
" arr[5:10]\n",
|
||
" \n",
|
||
" ```\n",
|
||
"\n",
|
||
"7. Variablen sollten nicht künstlich ausgerichtet werden.\n",
|
||
"\n",
|
||
" **So nicht:**\n",
|
||
" ```python\n",
|
||
" x = 4\n",
|
||
" y = 5\n",
|
||
" name = \"Lisa\"\n",
|
||
" \n",
|
||
" ```\n",
|
||
"\n",
|
||
"8. Die Namen `l`, `O` und `I` solltest du meiden, da sie leicht mit `i`, `0` und `L` verwechselt werden können. In Jupyter ist das meist kein Problem, aber gute Gewohnheiten schaden nie.\n",
|
||
"\n",
|
||
"Kurz: Halte es klar, lesbar und unkompliziert – der Rest ergibt sich von selbst."
|
||
]
|
||
},
|
||
{
|
||
"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 schon Funktionen gesehen haben, die direkt einen Rückgabewert liefern, schauen wir uns jetzt ein Python-Konzept an, das Daten „on the fly“ erzeugt: Generatoren.\n",
|
||
"\n",
|
||
"Oft denkt man zunächst, dass Funktionen wie `range` eine fertige Liste zurückgeben. Gedanklich ist das nicht völlig falsch – in der Umsetzung passiert aber etwas anderes.\n",
|
||
"\n",
|
||
"Mit einem simplen `print` können wir uns das schnell selbst bestätigen:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"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 eine Liste der Werte `0...9` ausgegeben zu bekommen, zeigt uns Python lediglich `range(0, 10)` an.\n",
|
||
"\n",
|
||
"Wenn man die Werte jedoch direkt ausgewertet sehen möchte, kann man den `*`-Operator verwenden:"
|
||
]
|
||
},
|
||
{
|
||
"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": [
|
||
"`range(10)` verändert dabei das Verhalten von `print`, indem es alle erzeugten Werte als einzelne Argumente einsetzt — also effektiv `print(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)` aufruft.\n",
|
||
"\n",
|
||
"Um selbst einen Generator zu bauen, nutzt man das Python-Keyword `yield`. Im Gegensatz zu einem normalen `return` stoppt `yield` die Ausführung nur kurz und setzt sie später an exakt dieser Stelle fort. Der Generator behält also einen veränderbaren Zustand.\n",
|
||
"\n",
|
||
"Die grundlegende Syntax sieht so aus:\n",
|
||
"\n",
|
||
"```python\n",
|
||
"def <funktions_name>(<parameterliste>):\n",
|
||
" # do something\n",
|
||
" yield <ergebnis>\n",
|
||
"```\n",
|
||
"\n",
|
||
"Eine einfache Generator-Funktion `count_to` könnte entsprechend so aussehen:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"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 0x7f0381936260>\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": "7adfeae1-bab7-42b8-b9f7-39c91cb5db0b",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-ca4e8d2864d7cff7",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"source": [
|
||
"Dasselbe lässt sich natürlich auch bequem mit einem `for`-Loop formulieren:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"id": "a19b17c4-144b-4197-8a71-f0554b467564",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-131bc31d10dd26c9",
|
||
"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 0x7f0381935e50>\n",
|
||
"This Generator evaluates to: 0 1 2 3 4 5 6 7 8 9\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"def count_to(n):\n",
|
||
" for i in range(n):\n",
|
||
" yield i\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\n",
|
||
"\n",
|
||
"*3 Punkte*\n",
|
||
"\n",
|
||
"Schreibe einen Generator `square_count` mit dem Eingabeparameter `n`, der die Quadratzahlen von $1 \\dots (n-1)^2$ erzeugt.\n",
|
||
"\n",
|
||
"Wichtig: Wenn `square_count` **kein** Generator ist, gibt es **0 Punkte**.\n",
|
||
"\n",
|
||
"Hinweis: Bei einer Eingabe von `5` sollen die Werte `1 4 9 16` ausgegeben werden.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"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(1, n):\n",
|
||
" yield i*i\n",
|
||
"# END SOLUTION"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"id": "234d782c-88f4-4bc2-a872-15a592ee1248",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "cell-a6c43a5365ad736d",
|
||
"locked": true,
|
||
"points": 3,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Square Numbers from 0 to 1: 1\n",
|
||
"Square Numbers from 0 to 2: 1 4\n",
|
||
"Square Numbers from 0 to 3: 1 4 9\n",
|
||
"Square Numbers from 0 to 4: 1 4 9 16\n",
|
||
"Square Numbers from 0 to 5: 1 4 9 16 25\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Hier werden die Loesungen getestet...\n",
|
||
"\n",
|
||
"# Check if the generator has the right name\n",
|
||
"assert \"square_count\" in dir() # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if square_count is a generator\n",
|
||
"import types\n",
|
||
"assert isinstance(square_count(1), types.GeneratorType) # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if the generator generates the right output\n",
|
||
"for n in range(10): \n",
|
||
" assert [i*i for i in range(1,n)] == [i for i in square_count(n)] # 1 Punkt\n",
|
||
"\n",
|
||
"# print\n",
|
||
"for n in range(2, 7):\n",
|
||
" print(f\"Square Numbers from 0 to {n-1}:\", *square_count(n))"
|
||
]
|
||
},
|
||
{
|
||
"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 erzeugen. Das funktioniert, weil die Werte erst zur Laufzeit berechnet werden – der Generator produziert also immer weiter, solange wir ihn laufen lassen.\n",
|
||
"\n",
|
||
"Damit eine Berechnung tatsächlich nie endet, muss die Schleifenbedingung dauerhaft `wahr` sein. Klassisch nutzt man dafür `while True:`. Da Python aber seine Eigenheiten hat, ist die kompakte Variante `while 1:` sogar etwas effizienter.\n",
|
||
"\n",
|
||
"Schauen wir uns nun ein Beispiel für einen unendlichen Generator an, der fortlaufend die nächste Fakultät berechnet und ausgibt:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"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 (!!): Wenn man diesen Generator direkt komplett auswertet, würde die Berechnung niemals enden. \n",
|
||
"Um stattdessen nur den nächsten Wert zu erhalten, stellt Python die Funktion `next` bereit – sie liefert jeweils den nächsten Zustand des Generators.\n",
|
||
"\n",
|
||
"In Kombination mit einem `for`-Loop und `next` lassen sich so ganz einfach die Fakultäten der Zahlen bis `5` ausgeben:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"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 Generators gespeichert bleibt, kann man ihn in der nächsten Zelle einfach erneut mit `next` aufrufen und erhält dann direkt $6! = 720$:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"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": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-3f6a4841c593371f",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"source": [
|
||
"### Aufgabe\n",
|
||
"\n",
|
||
"*3 Punkte*\n",
|
||
"\n",
|
||
"Schreibe einen Generator `naturals`, der bei jedem Aufruf die nächste natürliche Zahl ausgibt, beginnend mit 1.\n",
|
||
"\n",
|
||
"- Es sind **keine Eingabeparameter** erforderlich. \n",
|
||
"- Wenn die Funktion **kein Generator** ist, gibt es **0 Punkte**.\n",
|
||
"\n",
|
||
"*Hinweis*: Orientiere dich am Beispiel `faktoriel_gen`.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"id": "a8b2697c-706b-4744-a4b3-00682d6c95c3",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-09dd94e802cad9bc",
|
||
"locked": false,
|
||
"schema_version": 3,
|
||
"solution": true,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"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": 7,
|
||
"id": "403c70b9-3ac3-4a85-a302-d9c7d0585cbc",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "cell-e065f7326fb561ad",
|
||
"locked": true,
|
||
"points": 3,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, "
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Hier werden die Loesungen getestet...\n",
|
||
"\n",
|
||
"# Check if generator is named properly\n",
|
||
"assert \"naturals\" in dir() # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if naturals is a generator\n",
|
||
"import types\n",
|
||
"assert isinstance(naturals(), types.GeneratorType) # 1 Punkt\n",
|
||
"\n",
|
||
"# Test if generator works as intended\n",
|
||
"import random\n",
|
||
"test_n = random.randint(5, 17)\n",
|
||
"test_gen = naturals()\n",
|
||
"for i in range(1, test_n):\n",
|
||
" number = next(test_gen)\n",
|
||
" print(number, end=', ')\n",
|
||
" assert i == number # 1 Punkt"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "41bb0337-7194-4482-a5b9-af1243d4809c",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-bbe0bad0c47d41ae",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"source": [
|
||
"# Type Hints\n",
|
||
"\n",
|
||
"Mit [PEP 484](https://peps.python.org/pep-0484/) wurden in Python die sogenannten *Type Hints* eingeführt. Die Idee dahinter: eine standardisierte Möglichkeit, Ein- und Ausgabe von Funktionen klar zu kennzeichnen. Das verbessert unter anderem die Testbarkeit und Lesbarkeit von Python-Programmen deutlich.\n",
|
||
"\n",
|
||
"*Type Hints* zeigen also den Typ von Variablen und Funktionen an, z. B. `int`, `float`, `dict` usw.\n",
|
||
"\n",
|
||
"Die allgemeine Syntax sieht so aus:\n",
|
||
"\n",
|
||
"```python\n",
|
||
"def <funktions_name>(<parameter1>: <type>, <parameter2>: <type>) -> <output_type>:\n",
|
||
" # do something\n",
|
||
" return <value of defined output_type>\n",
|
||
"```\n",
|
||
"\n",
|
||
"Python ist dynamisch typisiert. Das bedeutet, der Typ einer Variable kann sich während der Laufzeit ändern.\n",
|
||
"Beispiel: Eine Ganzzahl `int` kann durch eine einfache Addition zu einer Fließkommazahl `float` werden:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"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 solche unerwarteten Typwechsel zu vermeiden, kann man *Type Hints* nutzen.\n",
|
||
"\n",
|
||
"Wichtig: Sie dienen nur als Hinweis und **garantieren nicht**, dass sich der Typ einer Variable tatsächlich ändert!\n",
|
||
"\n",
|
||
"Ein simples Beispiel: Eine Funktion, die einen Eingabewert als `int` annimmt und ihn in einen `str` umwandelt, könnte so aussehen:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"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": "ac0a9f33-2e9a-4779-8bf8-1004e6ed7177",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-36dc004fdb6c6682",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"source": [
|
||
"Um zu kennzeichnen, dass eine Variable einem bestimmten Datentyp zugeordnet ist, verwendet man die folgende Syntax:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"id": "e25d2f29-5857-4f0a-93d7-609201e8a0da",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-fdc340bdc4a2000e",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"name: str = \"Peter Parker\""
|
||
]
|
||
},
|
||
{
|
||
"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",
|
||
"Hier gehen wir nicht auf Klassen im Allgemeinen ein, sondern fokussieren uns auf ein spezielles Konzept, das mit [PEP 557](https://peps.python.org/pep-0557/) eingeführt wurde: **Dataclasses**.\n",
|
||
"\n",
|
||
"Standard-Datenstrukturen wie `dict`, `set`, `list` usw. sind mächtig und flexibel. Will man jedoch feste Datenstrukturen mit klar definierten Feldern verwenden, bietet sich das Modul `dataclasses` an.\n",
|
||
"\n",
|
||
"Dabei wird eine Klasse mit dem Keyword `class` erstellt und mit dem Decorator `@dataclasses.dataclass` ausgestattet. So lassen sich Datenobjekte mit festgelegter Struktur erzeugen.\n",
|
||
"\n",
|
||
"Zuerst importieren wir das Modul aus der Standardbibliothek:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"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": [
|
||
"Anschließend können wir eine Klasse erstellen. Zum Einstieg definieren wir eine Klasse `Person`, die die Werte `vorname` und `nachname` als Strings bereitstellt:\n",
|
||
"\n",
|
||
"**Wichtig:** Klassennamen beginnen in Python immer mit einem Großbuchstaben – Ausnahmen gibt es vor allem in der Standardbibliothek. \n",
|
||
"Ein kleines Beispiel: `range` sieht aus wie eine Funktion, ist intern jedoch eigentlich eine Klasse!\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"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": [
|
||
"Wenn wir nun ein Objekt der Klasse `Person` erstellen möchten, geht das so:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"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 man am Output sieht, ist die Variable `person` ein Objekt vom Typ `Person` und enthält die Werte `vorname='Eduard'` und `nachname='Jorswieck'`.\n",
|
||
"\n",
|
||
"Auf die einzelnen Felder der Dataclass kann man bequem über den `.`-Operator zugreifen:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"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": [
|
||
"Ein weiterer Vorteil von Dataclasses:\n",
|
||
"\n",
|
||
"Die Werte können direkt beim Erstellen des Objekts über die Feldnamen angegeben werden, unabhängig von der Reihenfolge:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"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": "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 stehen alle Werte beim Erstellen eines Objekts zur Verfügung. Um das abzufangen, können Standardwerte für die Felder definiert werden:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 21,
|
||
"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": [
|
||
"Wenn wir nun eine Dataclass ohne Eingabeparameter erstellen, übernimmt sie automatisch die definierten Standardwerte:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 22,
|
||
"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": "ccee5184-2f5e-4b90-99ae-2702405c0ec9",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-1c171ff9c43179be",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"source": [
|
||
"### Aufgabe\n",
|
||
"\n",
|
||
"*8 Punkte*\n",
|
||
"\n",
|
||
"Erstelle eine Dataclass `Student`.\n",
|
||
"\n",
|
||
"- Erstelle ein Objekt mit den Werten des Beispielstudenten und weise das Ergebnis von `asdict(<beispielstudent>)` der Variablen `stud` zu.\n",
|
||
"\n",
|
||
"- Die Dataclass soll die Werte `vorname`, `nachname`, `semester` und `mat_nr` speichern. Wählen Sie für jedes Attribut den **geeigneten Datentyp** (z. B. macht es Sinn, die Semesteranzahl als `int` zu speichern, nicht als `float`).\n",
|
||
"\n",
|
||
"- Importiere aus dem `dataclasses`-Modul die Funktion `asdict`. \n",
|
||
"\n",
|
||
"- **Wichtig:** Wenn `Student` keine Dataclass ist, gibt es **0 Punkte**.\n",
|
||
"\n",
|
||
"- Alle Variablen sollen mit `Type Hints` versehen werden.\n",
|
||
"\n",
|
||
"- Hat eines der Attribute keinen optimalen Datentyp, gibt es trotzdem Punkte, solange die Mehrheit korrekt ist.\n",
|
||
"\n",
|
||
"- Die Hinweise sind aktuell nicht in logischer Reihenfolge. \n",
|
||
" Struckturier den Code sinnvoll!\n",
|
||
" \n",
|
||
"Füge Kommentare ein, die sich auf die jeweilige [PEP 8](https://peps.python.org/pep-0008/) Regel beziehen.\n",
|
||
"\n",
|
||
"Beispielstudent:\n",
|
||
"\n",
|
||
"|Attribut|Wert|\n",
|
||
"|-|-|\n",
|
||
"|vorname|Martin|\n",
|
||
"|nachname|Le|\n",
|
||
"|mat_nr|520420|\n",
|
||
"|semester|5|"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"id": "d17b07db-7340-47c0-ab1d-38d75b3194c0",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-ac7d4ba80c4a0341",
|
||
"locked": false,
|
||
"schema_version": 3,
|
||
"solution": true,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# BEGIN SOLUTION\n",
|
||
"from dataclasses import asdict # Imports\n",
|
||
"\n",
|
||
"@dataclass\n",
|
||
"class Student:\n",
|
||
" vorname: str\n",
|
||
" nachname: str\n",
|
||
" mat_nr: int\n",
|
||
" semester: int \n",
|
||
"\n",
|
||
"stud: dict = asdict(Student(vorname='Martin', nachname='Le', mat_nr=520420, semester=5))\n",
|
||
"# END SOLUTION"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"id": "ab8dcc00-fc3f-4ae3-a18d-73f97e166ae3",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "cell-10b27d53a9659696",
|
||
"locked": true,
|
||
"points": 6,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'vorname': 'Martin', 'nachname': 'Le', 'mat_nr': 520420, 'semester': 5}\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Hier werden die Loesungen getestet...\n",
|
||
"\n",
|
||
"# Check if asdict is imported\n",
|
||
"assert \"asdict\" in dir() # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if Student is named properly\n",
|
||
"assert \"Student\" in dir() # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if Student is a Dataclassimport dataclasses\n",
|
||
"from dataclasses import is_dataclass\n",
|
||
"assert is_dataclass(Student) # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if stud is properly converted from Dataclass to dict\n",
|
||
"# 3 Punkt\n",
|
||
"assert stud == {'vorname': 'Martin', 'nachname': 'Le', 'mat_nr': 520420, 'semester': 5}\n",
|
||
"print(stud)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "448b2019-405f-4f3d-87d4-afa183363a35",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-b8ee343f30f40e1e",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"source": [
|
||
"### Aufgabe\n",
|
||
"\n",
|
||
"*6 Punkte*\n",
|
||
"\n",
|
||
"Gegeben sind zwei Listen `colorn` und `colorv_hex`, die zueinander indexsortiert sind.\n",
|
||
"\n",
|
||
"1. Erstelle eine Dataclass `Color` mit den Attributen `name` und `value` und versehe sie mit passenden Type Hints.\n",
|
||
"\n",
|
||
"2. Erzeuge anschließend eine Liste, die die Werte aus `colorn` und `colorv_hex` in Instanzen der Dataclass `Color` umwandelt, und speichere diese Liste in der Variablen `colors`.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 46,
|
||
"id": "27555b53-51e1-48b8-8962-528c22c85b02",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-8fb05db31c5a091d",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Diese Zelle vor bearbeiten der Aufgabe einmal ausführen\n",
|
||
"colorn = ['RED', 'GREEN', 'BLUE', 'YELLOW', 'PURPLE']\n",
|
||
"colorv_hex = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF']"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 63,
|
||
"id": "be9ea7b1-45c4-418d-9a5f-0d101c9fd245",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-dbfa5551c836768a",
|
||
"locked": false,
|
||
"schema_version": 3,
|
||
"solution": true,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"[Color(name='RED', value='#FF0000'), Color(name='GREEN', value='#00FF00'), Color(name='BLUE', value='#0000FF'), Color(name='YELLOW', value='#FFFF00'), Color(name='PURPLE', value='#FF00FF')]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"colors = None\n",
|
||
"# BEGIN SOLUTION\n",
|
||
"@dataclass\n",
|
||
"class Color:\n",
|
||
" name: str\n",
|
||
" value: str\n",
|
||
"\n",
|
||
"colors: list = [Color(n, w) for n, w in zip(colorn, colorv_hex)]\n",
|
||
" \n",
|
||
"print(colors)\n",
|
||
"# END SOLUTION"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 48,
|
||
"id": "cbf53cfd-7b06-443c-8aca-55463d77aa57",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "cell-a720d5d5ba2ea35c",
|
||
"locked": true,
|
||
"points": 6,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"[Color(name='RED', value='#FF0000'), Color(name='GREEN', value='#00FF00'), Color(name='BLUE', value='#0000FF'), Color(name='YELLOW', value='#FFFF00'), Color(name='PURPLE', value='#FF00FF')]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Hier werden die Loesungen getestet...\n",
|
||
"\n",
|
||
"# Check if Color is named properly\n",
|
||
"assert \"Color\" in dir() \n",
|
||
"\n",
|
||
"# Check if Color is a Dataclassimport dataclasses\n",
|
||
"from dataclasses import is_dataclass\n",
|
||
"assert is_dataclass(Color) # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if colors is a list\n",
|
||
"assert isinstance(colors, list) # 1 Punkt\n",
|
||
"\n",
|
||
"# Check if colors contains only Color Classes \n",
|
||
"for c in colors:\n",
|
||
" assert is_dataclass(c) # 1 Punkt\n",
|
||
"\n",
|
||
"### BEGIN HIDDEN TEST\n",
|
||
"@dataclass\n",
|
||
"class Color:\n",
|
||
" name: str\n",
|
||
" value: str\n",
|
||
"\n",
|
||
"c = [Color(n, w) for n, w in zip(colorn, colorv_hex)]\n",
|
||
"for r, s in zip(c, colors):\n",
|
||
" assert r.name == s.name\n",
|
||
" assert r.value == s.value\n",
|
||
"### END HIDDEN TEST\n",
|
||
"\n",
|
||
"print(colors) # 3 Punkte"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "7d970394-5982-4e4f-bcea-e72ce0e379c4",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Aufgabe\n",
|
||
"\n",
|
||
"*6 Punkte*\n",
|
||
"\n",
|
||
"Gegeben ist ein Dictionary `dict_obj`.\n",
|
||
"\n",
|
||
"1. Erstelle eine Dataclass `Transaction`, die die Originalstruktur des Dictionaries abbildet. \n",
|
||
" Achte dabei auf **korrekte Type Hints** für die einzelnen Attribute.\n",
|
||
"\n",
|
||
"2. Schreibe **unterhalb der Dataclass in einer Markdown-Zeile** deine Begründung, warum du diese Struktur gewählt hast. \n",
|
||
" **(!! Nicht als Kommentar !!)**"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 59,
|
||
"id": "b24c4d57-654c-4092-b8a1-1c942393e06a",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{ 'IBAN': 'DE19500211001598716891',\n",
|
||
" 'Transaction Number': 2469,\n",
|
||
" 'Recipient': { 'First Name': 'Phil',\n",
|
||
" 'Last Name': 'Keier'},\n",
|
||
" 'Amount': 3.14}\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Diese Zelle vor bearbeiten der Aufgabe einmal ausführen\n",
|
||
"import random\n",
|
||
"\n",
|
||
"dict_obj: dict = {\n",
|
||
" \"IBAN\": \"DE19500211001598716891\", # FAKE!\n",
|
||
" \"Transaction Number\": random.randint(1, 8000),\n",
|
||
" \"Recipient\": {\n",
|
||
" \"First Name\": \"Phil\",\n",
|
||
" \"Last Name\": \"Keier\",\n",
|
||
" },\n",
|
||
" \"Amount\": 3.14,\n",
|
||
"}\n",
|
||
"\n",
|
||
"# Just to show dict_obj\n",
|
||
"from pprint import pprint\n",
|
||
"pprint(dict_obj, depth=3, width=20, indent=2, sort_dicts=False)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 60,
|
||
"id": "e26ffb4c-237d-455d-b8b6-c6d652a7abf9",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# BEGIN SOLUTION\n",
|
||
"@dataclass\n",
|
||
"class Transaction:\n",
|
||
" iban: str\n",
|
||
" trans_nr: int\n",
|
||
" recipient: dict\n",
|
||
" amount: float\n",
|
||
"# END SOLUTION"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "9bf7abb7-88ab-405f-bed9-d637fd6046eb",
|
||
"metadata": {},
|
||
"source": [
|
||
"# BEGIN SOLUTION\n",
|
||
"# END SOLUTION"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 64,
|
||
"id": "920aabb3-81ed-48c2-90ed-6b5648058b18",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Name: iban, Typ: <class 'str'>\n",
|
||
"Name: trans_nr, Typ: <class 'int'>\n",
|
||
"Name: recipient, Typ: <class 'dict'>\n",
|
||
"Name: amount, Typ: <class 'float'>\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Hier werden die Loesungen getestet...\n",
|
||
"\n",
|
||
"# Assume Transaction is defined and a Dataclass\n",
|
||
"from dataclasses import is_dataclass\n",
|
||
"assert 'Transaction' in dir()\n",
|
||
"assert is_dataclass(Transaction)\n",
|
||
"\n",
|
||
"# Show the Transaction Obj Annotations\n",
|
||
"for field_name, field_type in Transaction.__annotations__.items():\n",
|
||
" print(f\"Name: {field_name}, Typ: {field_type}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "afe6543a-b41b-4d3c-9929-525a0463c6af",
|
||
"metadata": {
|
||
"editable": true,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "cell-a65f2c871406072c",
|
||
"locked": true,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
},
|
||
"slideshow": {
|
||
"slide_type": ""
|
||
},
|
||
"tags": []
|
||
},
|
||
"source": [
|
||
"# Walrus Operator – Assignment Expressions\n",
|
||
"\n",
|
||
"Der sogenannte *Walrus Operator* `:=` wurde mit [PEP 572](https://peps.python.org/pep-0572/) eingeführt. \n",
|
||
"Interessanter Fun Fact: Einer der Gründe, warum Guido van Rossum das Python-Projekt verlassen hat, hängt mit der Kontroverse um diesen Operator zusammen. \n",
|
||
"\n",
|
||
"Aus dem offiziellen 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",
|
||
"Der Operator selbst fügt keine neue Funktionalität hinzu. Er erlaubt lediglich, eine **Zuweisung während einer Auswertung** vorzunehmen.\n",
|
||
"\n",
|
||
"Ein kurzes Beispiel – bitte nicht nachmachen! Niemand, wirklich niemand, möchte das im echten Code sehen:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 30,
|
||
"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": [
|
||
"Liste ist lang genug: 4\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# Zuweisung während einer Bedingung\n",
|
||
"if (n := len([1, 2, 3, 4])) > 3:\n",
|
||
" print(f\"Liste ist lang genug: {n}\")"
|
||
]
|
||
},
|
||
{
|
||
"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",
|
||
"Der Walrus Operator `:=` wird sinnvoll eingesetzt, wenn man vermeiden möchte, dass eine teure Berechnung **zweimal ausgeführt** wird.\n",
|
||
"\n",
|
||
"Klassisches Beispiel ohne Walrus: `n + 1` wird zweimal berechnet:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 31,
|
||
"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 dem Walrus Operator lässt sich die Berechnung `n + 1` direkt im `if` einer Variablen `out` zuweisen:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 32,
|
||
"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": [
|
||
"Auch ohne Walrus Operator lässt sich verhindern, dass `n + 1` zweimal berechnet wird, indem man die Berechnung vorher einer Variablen zuweist:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 33,
|
||
"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 grundsätzlich davon ab, den Walrus Operator `:=` zu verwenden. \n",
|
||
"Meiner Erfahrung nach macht er den Code oft unlesbar und spart bestenfalls nur 2–3 Zeilen. In meinen eigenen Projekten gab es nie einen echten Grund, ihn einzusetzen – er brachte nie einen spürbaren Vorteil bei Berechnungen. \n",
|
||
"\n",
|
||
"Dennoch wollte ich dir einmal demonstrieren, wie der Walrus Operator funktioniert, damit du seine Anwendung siehst."
|
||
]
|
||
}
|
||
],
|
||
"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.11.6"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|