diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..304e4a9 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-toolsai.jupyter", + "ms-python.python" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} \ No newline at end of file diff --git a/docs/000Hello.py b/docs/000Hello.py deleted file mode 100644 index 7178a5b..0000000 --- a/docs/000Hello.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Willkommen zum Python Ausbildungsproramm. -Java solltest du bereits kennen. Begriffe wie Variable, Array, Methode sind dir keine Fremdwörter. - -Python ist einer der beliebtesten Programmiersprachen. Sie wurde 1991 von Guido van Rossum, einem niederländischen Softwareentwickler erschaffen. -Python wird oft in folgenden Bereichen angewendet: - Web development (server seitig) - Software entwicklung - mathematische Anwendungen - Systemskripting - -Eine Einzigartigkeit von Python ist die Leserlickeit. Python liest sich fast wie die englische Sprache, das wirst im Verlauf der Einführung noch merken. - -Wir starten nun direkt mit dem Klassiker, dem Hello World Programm. -""" - -###################################################################### - - -print("Hello World!") # Output: Hello World! - - -###################################################################### -""" -Ziemlich simpel, oder? Die Methode print() nimmt einen String und gibt ihn in der Kommandozeile aus. -Jede print() Anweisung schreibt dabei auf eine neue Zeile. Beachte, das in Python keine Semikolons benutzt werden. -Jedes Statement im Code wird hierbei auf eine neue Zeile geschrieben, ohne Semikolon. -Kommentare werden mit einem # Zeichen eingeleitet. -Eine main Methode wie in vielen anderen Programmiersprachen braucht es nicht. -""" - -""" -Ein sehr wichtiger Punkt, welcher Python von praktisch allen anderen Programmiersprachen unterscheidet ist, -dass Code-Blöcke nicht durch geschweifte Klammern (also "{" und "}") abgetrennt werden, sondern durch Einrückungen. - -Hier ein kleines Beispiel: -""" - -###################################################################### - -if True: - print("x is 1.") - -while False: - if True: - print("Hallo") - else: - break - -###################################################################### - - -# https://www.learnpython.org/en/String_Formatting -# https://www.learnpython.org/en/Basic_String_Operations -# if, elif, else -# generators -# input -# imports -# set https://www.w3schools.com/python/python_tuples.asp -# tuple https://www.w3schools.com/python/python_tuples.asp - - -# https://www.w3schools.com/python/python_variables_output.asp - - -# https://www.youtube.com/watch?v=dXkWy58MV2Y diff --git a/docs/010Variablen.py b/docs/010Variablen.py deleted file mode 100644 index cd9be92..0000000 --- a/docs/010Variablen.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -In Python werden variablen initialisiert, sobald sie einen Wert zugewiesen bekommen. -Es benötigt weder die Verwendung eines Keywords (wie z.B. new), Noch die Angabe eines Datentypen. -Dies ist nämlich nicht erforderlich, da alle variablen als Objekte angesehen werden. Python bestimmt im Hintergrund, um welchen Datentyp es sich hierbei handelt. - -Das Einfachste Beispiel lautet ganz einfach x = 5 - -In Python ist es üblich ein sogenanntes Snake Case naming zu wählen, nicht wie in Java Camel oder Pascal Case. -Dabei werden Wörter einfach mit einem Unterstrich (also "_") getrennt. -Variablennamen starten wie üblich mit einem Kleinbuchstaben, Klassen mit einem Grossbuchstaben. - -Einige Beispiele für die Initialisierung und Operationen mit Variablen: -""" - -###################################################################### - -my_int = 7 -print(my_int) # Output: 7 - -my_float = 7.0 -print(my_float) # Output: 7.0 - -my_float = float(my_int) # Typ-casting von int zu float -print(my_float) # Output: 7.0 - -my_string1 = 'Hello' # Initialisierung eines Strings mit ' -my_string2 = "Hello" # Initialisierung eines Strings mit " - -# Initialisierung mit ", der Apostroph wird hier als character angesehen. -my_string3 = "Don't worry about apostrophes" -# Initialisierung mit ', Anführuns- und Schlusszeichen werden hier als character angesehen. -my_string4 = 'I just want to say "Hello"' - -my_int2 = 1 -my_int3 = 2 -my_int4 = my_int2 + my_int3 -print(my_int4) # Output: 3 - - -hello = "hello" -world = "world" -hello_world = hello + " " + world -print(hello_world) # Output: hello world - - -###################################################################### - -""" -Aber Vorsicht! Zahlen können nicht mit Strings addiert werden! Folgender Code wirft einen Error: -""" - -###################################################################### - -# This will not work! -my_number = 1 -my_string = "hello" - -# print(my_string + my_number) # TypeError - -###################################################################### - -""" -Der Fehler wird hier zu Laufzeit des Programms geworfen, der Compiler bemerkt den Fehler nämlich nicht. -Die Lösung des Problems sind sogenannte formatted Strings oder einfach f-Strings. Eine Möglichkeit ist die folgende: -""" - -###################################################################### - -my_number = 1 -my_string = "hello" - -print(f'my_number is {my_number} and my_string is "{my_string}"') -#Output: my_number is 1 and my_string is "hello" - -###################################################################### - -""" -Ein weiteres cooles Feature in Python ist die Mehrfachzuweisung. Folgende Beispiele sind valider Python code: -""" - -###################################################################### - -x, y, z = 10, 41, 5.0 -a = b = c = "Hello" - -###################################################################### - - -""" -Hier wurde x der Wert 10, y der Wert 41 und z den Wert 5.0 zugewiesen. -In der zweiten Zeile wurde den Variablen a, b und c jeweils der String "Hello" zugewiesen. -""" - - -""" -Zusatz: Casting - -Wie auch in anderen Programmiersprachen können Datentypen gecastet werden. -Folgend einige Beispiele, das Prinzip sollte klar sein: -""" - -###################################################################### - -a = str(100) # a = '100' -b = int(4.2) # b = 4 -c = float(7) # c = 7.0 -d = int('42') # d = 42 - -###################################################################### - - diff --git a/docs/013Input_Output.py b/docs/013Input_Output.py deleted file mode 100644 index 81272ff..0000000 --- a/docs/013Input_Output.py +++ /dev/null @@ -1 +0,0 @@ -# https://www.w3schools.com/python/python_variables_output.asp diff --git a/docs/015Strings.py b/docs/015Strings.py deleted file mode 100644 index 34e61d0..0000000 --- a/docs/015Strings.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Strings können in Python mit einfachen oder doppelten Anführungszeichen initialisiert werden. -Ebenfalls ist es möglich, Strings über mehrere Zeilen mit dreifach doppelten oder dreifach einfachen Anführungszeichen zu initialisieren. -Hierbei werden die Leer- und Enterzeichen mitgespeichert. Ein String über 3 Zeilen wird bei einem print() Aufruf auch über 3 Zeilen in der Konsole ausgegeben. -Im nachfolgenden Code findest du ein paar Beispiele: -""" -###################################################################### - -random_string = "Hello" -random_string2 = 'World' - -random_multiline_string = """Say -my -name!""" - -random_multiline_string2 = '''Hello -my name is -Jeff''' - -print(random_multiline_string2) -# Output: -# Hello -# my name is -# Jeff - -###################################################################### - -""" -Strings sind wie Arrays! Man kann auf die Elemente (Character; Buchstaben) mit dem [] Operator zugreifen. -Ebenfalls kann man mit Loops über Strings iterieren oder die die Länge des Strings ausgeben lassen. -Welche Loops es gibt und wie man sie implementiert wird in einem späteren Kappitel behandelt. -Einige Operationen mit Strings sind im nachfolgenden Code zu finden: -""" - -###################################################################### - -a = "Hello World!" -print(len(a)) # 12 -print(a[1]) # e - -# Jedes Zeichen wird auf einer separaten Zeile ausgegeben: -for x in a: - print(x) - -print("orl" in a) # True -print("i" not in a) # True -###################################################################### - -""" -Eine weitere coole Operation auf Strings ist das sogenannte "Slicin". -Wie der Name schon suggeriert, kann man Strings in verschiedene Teile zerlegen. Dabei nehmen wir aber nicht einzelne Buchstaben wie vorher, -sondern ganze Teile. Einige slicing Operationen sind: -""" - -###################################################################### - -beispiel = "Python ist eine tolle Programmiersprache!" - -# Herausschneiden zwischen dem ersten und zweiten Index. -# Beachte, dass der Buchstabe mit dem 2. Index nicht inklusive ist. -print(beispiel[2:12]) # thon ist ei -print(beispiel[2:4]) # th - -# Herausschneiden der ersten Buchstaben -print(beispiel[:5]) # Pytho -print(beispiel[:20]) # Python ist eine toll - -# Weglassen der ersten Buchstaben -print(beispiel[4:]) # on ist eine tolle Programmiersprache! -print(beispiel[22:]) # Programmiersprache! - -# Start beim 8. letzten Buchstaben, Ende beim 5. letzten Buchstaben. -print(beispiel[-8:-5]) # spr -print(beispiel[-19:-10]) # Programmi - -###################################################################### - -""" -Wie auch in Java hat Python einige vordefinierte Methoden auf Strings. Einige sind nachfolgend Aufgelistet: -Falls du noch weitere Methoden sehen möchtest gibt es hier eine grosse Liste: https://www.w3schools.com/python/python_strings_methods.asp -""" - -###################################################################### - -s = "Python is fun!" - -print(s.upper()) # PYTHON IS FUN! -print(s.lower()) # python is fun! - -# Ersetzen eines Substrings mit einem anderen Substring -print(s.replace("n", "m")) # Pythom is fum! -print(s.replace(" ", "#")) # Python#is#fun! -print(s.replace("Python", "Java")) # Java is fun! - -# .strip() entfernt alle Leerzeichen am Anfang und am Ende -print(" to many whitespaces ".strip()) # to many whitespaces - -# .split() gibt eine Liste mit substrings zurück. -print(s.split(" ")) # ['Python', 'is', 'fun!'] -print(s.split("n")) # ['Pytho', ' is fu', '!'] -print(s.split("is")) # ['Python ', ' fun!'] - -# Strings können mit dem + Operator aneinandergeheftet werden. -print(s + " Java too!") # Python is fun! Java too! - -###################################################################### - -""" -Escape Character - -Um Zeichen in einem String hinzufügen zu können, welche normalerweise nicht erlaubt sind, kann ein \ verwendet werden. -Es gibt auch weitere spezielle Zeichen, welche mit Hilfe eines Backslashes in einen String eingefügt werden können. -""" - -###################################################################### - -# ' muss nicht escaped werden, " aber schon. -print("Es gibt einfache (') und doppelte (\") Anführungszeichen") -# Hier ist es genau umgekehrt -print('Es gibt einfache (\') und doppelte (") Anführungszeichen') -# Escapen des Escape characters -print("Ich verwende ein \\, um zu escapen") - -###################################################################### - -""" -\n newline -\t tab -""" diff --git a/docs/01_basics/000Intro.ipynb b/docs/01_basics/000Intro.ipynb new file mode 100644 index 0000000..333fcad --- /dev/null +++ b/docs/01_basics/000Intro.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Python Ausbildungsproramm\n", + "Willkommen zum Python Ausbildungsproramm.\n", + "\n", + "Python ist einer der beliebtesten Programmiersprachen. Sie wurde 1991 von Guido van Rossum, einem niederländischen Softwareentwickler erschaffen.\n", + "Python wird oft in folgenden Bereichen angewendet:\n", + "\n", + "* Web development (server seitig)\n", + "* Softwareentwicklung\n", + "* mathematische Anwendungen\n", + "* Systemskripting\n", + "\n", + "Eine Einzigartigkeit von Python ist die Lesbarkeit. Python liest sich fast wie die englische Sprache, das wirst im Verlauf der Einführung noch merken. Ausserdem war Python so konzipiert, dass die Zeit, die für die Entwicklung benötigt wird, möglichst kurz gehalten wird. Daher war auch das Ziel, dass Python eine leicht verständliche Programmiersprache wird.\n", + "\n", + "Ausserdem ist Python eine \"Open Source\"-Programmiersprache, den Source-Code von Python könntest du z.B. hier nachschauen gehen, wenn es dich interessieren sollte: https://www.python.org/downloads/source/" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Jupyter-Notebook\n", + "\n", + "Dieses Dokument hier ist ein Jupiter-Notebook. Das erkennst du an der Dateiendung \".ipynb\", was so viel bedeutet wie \"integrated Python Notebook\".\n", + "\n", + "Das coole an solchen Jupyter-Notebooks ist, dass du formatierten Text (im Markdown-Format) mit Python-Code kombinieren kannst, und Python-Code durch Klick auf den Play-Button direkt ausführen und debuggen kannst.\n", + "\n", + "Jupyter-Notebooks bestehen grundsätzlich aus diesen beiden Komponenten:\n", + "1. Markup Cells (Markup-Blöcke)\n", + "und\n", + "2. Code Cells (Code-Blöcke)\n", + "\n", + "Du kannst von beiden kreuz und quer in einem Jupyter-Notebook hinzufügen.\n", + "\n", + "Neue Blöcke kannst du zwischen, vor und nach jedem Block hinzufügen via einen Klick auf \"+ Code\" bzw. \"+ Markdown\"." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Code-Block\n", + "Nachfolgend siehst du einen Code-Block mit Python-Code drin.\n", + "\n", + "Damit du den Code ausführen kannst, benötigst du mindestens folgendes in VS Code:\n", + "* Eine aktive Python-Installation. Python kannst du von einer der beiden Quellen herunterladen:\n", + " * https://www.microsoft.com/store/productId/9NRWMJP3717K oder hier herunterladen.\n", + " * https://www.python.org/downloads/\n", + "* Die Jupyter-Extension für VS Code. In VS Code kannst du diese herunterladen, indem du [Ctrl] + [Shift] + [X] klickst, das öffnet das Extensions-Tab auf der Seite, dort suchst du nach \"Jupyter\" und installierst die gleichnamige Extension von Microsoft. Möglicherweise wurdest du bereits gefragt, ob du diese Extension installieren möchtest, da diese in der Datei für vorgeschlagene Extensions (\".vscode/extensions.json\") vorkommt.\n", + "* Die Python-Extension von Microsoft. Diese sollte bereits nach der Installation der Jupyter-Extension installiert worden sein.\n", + "\n", + "Anschliessend solltest du in der Lage sein, Code-Blöcke auszuführen. Möglicherweise fehlt dir noch ein Jupyter-Kernel, das wirst du aber gleich herausfinden. Klicke im Nachfolgenden Code-Block auf den Play-Button und wenn dich VS Code unten rechts fragt, ob es etwas installieren oder erstellen soll, dann bestätige dies.\n", + "\n", + "Wenn du nach einem Python-Environment gefragt wirst, dann wähle diejenige Python-Version aus, die du als letztes installiert hast (also eine aktuelle, die neuer als 3.9 ist).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import getpass\n", + "\n", + "username = getpass.getuser()\n", + "print(f\"Hello {username}, nice to see you!\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unterhalb des Code-Block solltest du nun eine Begrüssungs-Nachricht sehen, wo dein Benutzer-Name vorkommt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Markup-Blöcke\n", + "\n", + "Markup-Blöcke bestehen im Prinzip nur aus formatiertem Text.\n", + "\n", + "Die Formatierung wird mit bestimmten Zeichen realisiert. Versuche dies gerade aus:\n", + "* Klicke unterhalb dieses Blockes auf \"+ Markdown\".\n", + "* Gib einen Text ein, z.B. `Hello **World**, nice to see *you*.`.\n", + "* Klicke oben rechts bei der Zelle/beim Block auf `▶️`-Symbol oder klicke [Shift] + [Enter].\n", + "\n", + "Nun sollte dein Text formatiert erscheinen.\n", + "* Mit zwei Sternchen markierst du fetten Text: `**World**`\n", + "* Mit einem Sternchen markierst du kursiven Text: `*you*`\n", + "* Mit `#`, `##`, `###` usw. markierst du überschriften: `# Überschrift 1`\n", + "\n", + "Die Formatierung ist \"Markdown\". In VS Code wird das Markdown für GitHub standardmässig verwendet (unterscheidet sich von demjenigen, das Atlassian z.B. in Jira einsetzt). Eine Übersicht, wie du Text in Markdown formatieren kannst, findest du hier: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Python\n", + "### Erstes Beispiel\n", + "Wir starten nun direkt mit dem Klassiker, dem Hello World Programm.\n", + "\n", + "Führe folgenden Code aus, indem du auf den Play-Button links von der Code-Zelle klickst (\"Execute Cell\").\n", + "\n", + "(Alternativ kannst du auch einfach in den Programm-Code klicken und `[Shift]+[Enter]` bzw. `[Ctrl]+[Alt]+[Enter]` für Debugging klicken.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Hello World!\") # Output: Hello World!" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ziemlich simpel, oder?\n", + "\n", + "Die Methode `print()` nimmt einen Text (nachfolgend String genannt) und gibt ihn in in der Konsole nachher aus.\n", + "\n", + "Jede `print()`-Anweisung schreibt dabei auf eine neue Zeile.\n", + "\n", + "Jedes Statement im Code wird hierbei auf eine neue Zeile geschrieben, ohne Semikolon.\n", + "\n", + "Möchtest du den Code kommentieren, dann kannst du Kommentare mit einem `#`-Zeichen (Hashtag, \"Gartenhag\") einleiten. In den Kommentaren kannst du alles schreiben, was du willst. Diese dienen oft dazu, etwas Komplizierteres zu beschreiben. Kommentare sollten aber nicht als Ersatz für schlecht strukturierten oder schwer verständlichen Code verwendet werden. Stattdessen sollten sie dazu verwendet werden, um zu erklären, warum etwas so implementiert wurde, insbesondere in Fällen, in denen dies nicht offensichtlich ist.\n", + "\n", + "Der Code wird dann von Zeile nach Zeile abgearbeitet. Im Gegensatz zu anderen Programmiersprachen wie Java, können die Anweisungen direkt auf oberster Ebene in eine Datei geschrieben werden, ohne dass du diese noch irgendwo reinpacken musst (also keine Methoden/Funktionen/Klassen erforderlich).\n", + "\n", + "Ein sehr wichtiger Punkt, welcher Python von praktisch allen anderen Programmiersprachen unterscheidet ist, \n", + "dass Code-Blöcke nicht durch geschweifte Klammern `{}` abgetrennt werden, sondern durch Einrückungen.\n", + "\n", + "Hier ein kleines Beispiel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if True:\n", + " print(\"Hallo :)\")\n", + "\n", + "while False:\n", + " if True:\n", + " print(\"Bonjour\")\n", + " else:\n", + " print(\"Ciao\")\n", + " break" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Java und JavaScript z.B. würde der gleiche Code wie folgt aussehen. Beachte, dass die geschweiften Klammern (`{` und `}`) in Python mit einem Doppelpunkt (`:`) und dessen innerer Block zwingend mit Einrückungen (Tab oder 4 Leerzeichen) realisiert werden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "javascript" + } + }, + "outputs": [], + "source": [ + "if (true) {\n", + " console.log(\"Hallo :)\");\n", + "}\n", + "\n", + "while (false) {\n", + " if (true) {\n", + " console.log(\"Bonjour\");\n", + " } else {\n", + " console.log(\"Ciao\");\n", + " break;\n", + " }\n", + "}" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Python-Code ausführen\n", + "\n", + "Es gibt verschiedene Arten, wie du Python-Code schnell testen kannst:\n", + "1. In einer Jupyter-Notebook-Code-Zelle (wie hier),\n", + "2. mit dem bei der Python-Installation dazu installierten IDLE,\n", + "3. und im Terminal mit dem `python`-, `python3`-, `python3.XX`- oder `py`-Befehl.\n", + "\n", + "Punkt 1 kennst du bereits schon. Deswegen wird dieser Punkt nicht weiter vertieft. Wenn du kein Jupyter-Notebook verwendest, sind die anderen beiden Optionen eine gute Alternative." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### IDLE\n", + "Wenn du Python von der [offiziellen Website](https://www.python.org/downloads/) herunterlädst, dann installierst du auch gleich das Programm \"IDLE\" mit.\n", + "\n", + "Dieses Programm kannst du einfach starten und dann sollte ein solches Fenster erscheinen:\n", + "\n", + "![asdf](../../ressources/images/docs/01_basics/idle_shell.png)\n", + "\n", + "Auf der letzten Zeile kannst du dann direkt Python-Code eingeben." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python im Terminal\n", + "\n", + "Python kannst du mit mind. 1 dieser Befehle ausführen (abhängig von der Installation und des Betriebssystems):\n", + "* `python`\n", + "* `python3`\n", + "* `python3.XX`\n", + "* `py`\n", + "\n", + "![Python im Terminal](../../ressources/images/docs/01_basics/python_in_terminal.png)\n", + "\n", + "Mit dem Befehl kannst du natürlich auch ganze Dateien ausführen lassen:\n", + "```shellscript\n", + "python3.11 path/to/my_file.py\n", + "```\n", + "\n", + "Nachteil der Shell ist, dass du keine Vorschläge hast." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/01_basics/010Variablen.ipynb b/docs/01_basics/010Variablen.ipynb new file mode 100644 index 0000000..abfec5e --- /dev/null +++ b/docs/01_basics/010Variablen.ipynb @@ -0,0 +1,102 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Variablen in Python\n", + "\n", + "In Python werden Variablen initialisiert, sobald sie einen Wert zugewiesen bekommen.\n", + "\n", + "Im folgenden Beispiel wird der Variable `x` den Wert `5` zugewiesen: \n", + "```python\n", + "x = 5\n", + "```\n", + "\n", + "Es benötigt weder die Verwendung eines Keywords (wie z.B. `var`, `const`, `new`), noch die Angabe eines Datentypen.\n", + "\n", + "\n", + "In Python ist es üblich, ein sogenanntes **snake_case**-Naming zu wählen, nicht wie in Java das \"camelCase\" oder \"PascalCase\".\n", + "* Dabei werden Wörter mit einem Unterstrich (also \"_\") getrennt. \n", + "* Variablennamen starten wie üblich mit einem Kleinbuchstaben, Klassen mit einem Grossbuchstaben. Der Name von Konstanten besteht nur aus Grossbuchstaben, einzelne Wörter werden mit einem Unterstrich kombiniert.\n", + "\n", + "Einige Beispiele für die Initialisierung von Variablen: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MY_INT_CONSTANT = 7\n", + "print(MY_INT_CONSTANT) # Output: 7\n", + "\n", + "my_float = 7.0\n", + "print(my_float) # Output: 7.0\n", + "\n", + "my_float = float(MY_INT_CONSTANT) # Typ-casting von int zu float\n", + "print(my_float) # Output: 7.0\n", + "\n", + "my_string1 = 'Hello' # Initialisierung eines Strings mit '\n", + "my_string2 = \"Hello\" # Initialisierung eines Strings mit \"\n", + "\n", + "# Initialisierung mit \", der Apostroph wird hier als Character angesehen:\n", + "my_string3 = \"Don't worry about apostrophes\"\n", + "# Initialisierung mit ', Anführungs- und Schlusszeichen werden hier als Character angesehen:\n", + "my_string4 = 'I just want to say \"Hello\"'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ein weiteres cooles Feature in Python ist die Mehrfachzuweisung. Folgendes ist valider Python-Code:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x, y, z = 10, 41, 5.0\n", + "a = b = c = \"Hello\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hier wurde `x` der Wert `10`, `y` der Wert `41` und `z` den Wert `5.0` zugewiesen.\n", + "\n", + "In der zweiten Zeile wurde den Variablen `a`, `b` und `c` jeweils der String `\"Hello\"` zugewiesen." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/01_basics/011_Typen.ipynb b/docs/01_basics/011_Typen.ipynb new file mode 100644 index 0000000..d113fdb --- /dev/null +++ b/docs/01_basics/011_Typen.ipynb @@ -0,0 +1,403 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Typen in Python" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zahlen\n", + "\n", + "In Python kannst du Variablen von Zahlen wie folgt erstellen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "zahl1 = 1\n", + "zahl2 = 2" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Und ausserdem kannst du auf Zahlen bestimmte Operationen durchführen, z.B. eine Addition.\n", + "\n", + "In nächsten Beispiel erstellen wir neue Variablen `zahl3`, die dem Resultat aus der Addition von den ersten beiden Zahlen `zahl1` und `zahl2` entspricht:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "zahl3 = zahl1 + zahl2\n", + "print(zahl3) # Output: 3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Im obigen Beispiel haben wir folgendes in der Variable `zahl3` gespeichert:\n", + "\n", + "```python\n", + "zahl3 = 1 + 2\n", + "```\n", + "\n", + "Folglich führt `print(zahl3)` zu folgender Ausgabe:\n", + "```\n", + "3\n", + "```\n", + "\n", + "\n", + "Wir haben also gesehen, dass wir Zahlen addieren können. Folgende Operationen sind zudem möglich:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Addition\", 1 + 2 + 3) # Keine der Operationen ist auf 2 Zahlen begrenzt.\n", + "print(\"Subtraktion\", 10 - 1)\n", + "print(\"Multiplikation\", 7 * 8)\n", + "print(\"Division\", 10 / 2)\n", + "print(\"Hochrechnen\", 5**2)\n", + "\n", + "# Zudem kannst du diese Operationen auch kombinieren:\n", + "print(\"Add und Sub\", 1 + 2 - 3)\n", + "print(\"Punkt vor Strich\", 1 + 2 * 3)\n", + "print(\"Klammern\", (1 + 2) * 3)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Zahlen-Typen\n", + "Ist dir aufgefallen, dass alle Operationen eine Ganzzahl zurückgegeben haben abgesehen von der Division?\n", + "\n", + "```python\n", + "print(\"Division\", 10 / 2)\n", + "```\n", + "\n", + "Der Grund hierfür ist für dich sicher offensichtlich: Die Division ist die einzige Operation, die aus zwei Ganzzahlen eine Zahl mit Kommastellen generieren könnte. Die Division `10 / 2` würde mathematisch gesehen zwar eine Ganzzahl zurückgeben, aber Python gibt bei einer Division immer eine Fliesskommazahl zurück.\n", + "\n", + "Und das ist auch das Praktische an Python! In anderen Programmiersprachen hättest du eine abgerundete Ganzzahl erhalten. Daher musst du dich in Python nicht zwingend mit den verschiedenen Zahlen-Typen auseinander setzen.\n", + "\n", + "Lasse uns diese trotzdem rasch anschauen:\n", + "\n", + "* Ganzzahlen sind `int`s.\n", + "* Fliesskommazahlen sind meistens `float`.\n", + "\n", + "Möchtest du bei einer Variable angeben, dass sie eine Ganzzahl ist, dann kannst du das wie folgt tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ganz_zahl: int = 3\n", + "komma_zahl: float = 6.0" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Bitte beachte im obigen Beispiel, dass die Angabe von `: int` gar nichts macht. Diese Angabe dient dem/der Entwickler:in." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## String (Text)\n", + "Du kannst wie folgt Variablen mit Text-Inhalt erstellen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hello = \"hello\"\n", + "world = \"world\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ausserdem kannst du einzelne solche Text-Variablen miteinander kombinieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hello_world = hello + \" \" + world\n", + "print(hello_world) # Output: hello world" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Du kannst auch Zahlen beim Kombinieren von Variablen mit Strings verwenden. Folgender Code wird nicht funktionieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This will not work!\n", + "my_number = 1\n", + "my_string = \"hello\"\n", + "\n", + "print(my_string + my_number) # TypeError" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Der Fehler wird hier zu Laufzeit des Programms geworfen, der Compiler bemerkt den Fehler nämlich nicht.\n", + "Die Lösung des Problems sind sogenannte formatted Strings oder einfach f-Strings. Eine Möglichkeit ist die folgende:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_number = 1\n", + "my_string = \"hello\"\n", + "\n", + "# Output: my_number is 1 and my_string is \"hello\"\n", + "print(f'my_number is {my_number} and my_string is \"{my_string}\"')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Oder, du \"castest\" die Zahlen zu Strings (die `str(...)`-Funktion wandelt etwas in einen String um.):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('my_number is \"' + str(my_number) + '\" and my_string is \"' + my_string + '\"')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du bei einer String-Variable angeben, dass es sich dabei um einen String handelt, dann kannst du mit der Typangabe `: str` tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_string: str = \"Hello World\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Booleans\n", + "\n", + "Der letzte zentrale Datentyp in Python sind sogenannte Booleans.\n", + "\n", + "Ein Boolean kann genau 1 von 2 Werten einnehmen:\n", + "* `True`, bedeutet wahr bzw. zutreffend\n", + "* `False` für nicht-zutreffend.\n", + "\n", + "Eine Boolean-Variable kannst du wie folgt definieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bool1 = True\n", + "bool2 = False\n", + "\n", + "bool3 = bool1 and bool2\n", + "bool4 = bool1 or bool2\n", + "\n", + "print(bool1, bool2, bool3, bool4)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Oft verwendest du Booleans im Zusammenhang mit Bedingungen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if bool1:\n", + " print(\"There we go!\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Bedingungen werden (meistens) Booleans angegeben: Wenn dieser Wert gleich `True` ist, wird der innere Block ausgeführt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Im ersten Beispiel zu den Booleans hast du auch solche Operationen auf Booleans gesehen:\n", + "\n", + "```python\n", + "bool3 = bool1 and bool2 # False\n", + "bool4 = bool1 or bool2 # True\n", + "```\n", + "\n", + "* Ein `and` gibt `True` zurück, wenn beide Werte `True` sind, ansonsten `False`.\n", + "* Ein `or` gibt `True` zurück, wenn **mindestens** eine der beiden Werte `True` ist, ansonsten `False`.\n", + "\n", + "Du kannst auch aus einer Zahl einen Boolean generieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bool5 = bool(0)\n", + "bool6 = bool(1)\n", + "bool7 = bool(2)\n", + "bool8 = bool(-1)\n", + "\n", + "print(bool5, bool6, bool7, bool8)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Funktion `bool(...)` gibt für alle Zahlen $\\neq 0$ den Wert `True` zurück, für `0` den Wert `False`." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Casting\n", + "\n", + "Du wirst in die Situation kommen, wo du z.B. einen String hast, du aber eine `int`-Variable haben musst.\n", + "\n", + "Hier kommst das \"Casting\" ins Spiel. Damit kannst du einen Wert eines Types in einen Wert eines anderen Types umwandeln.\n", + "\n", + "Versuche bitte, folgendes Beispiel zu verstehen. Wenn du Schwierigkeiten damit hast, dann frage bitte nach." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = str(100) # a = '100'\n", + "b = int(4.2) # b = 4\n", + "c = float(7) # c = 7.0\n", + "d = int('42') # d = 42\n", + "\n", + "print(a, b, c, d)\n", + "\n", + "# type(...) gibt dir an, was für ein Typ die Variable hat:\n", + "print(type(a), type(b), type(c), type(d))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/01_basics/013Input_Output.ipynb b/docs/01_basics/013Input_Output.ipynb new file mode 100644 index 0000000..4d17d11 --- /dev/null +++ b/docs/01_basics/013Input_Output.ipynb @@ -0,0 +1,77 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Eingabe in Python\n", + "Die einfachste Möglichkeit, in Python Eingaben zu erhalten, besteht darin, die `input()` Funktion zu verwenden. Diese Funktion wird verwendet, um Benutzereingaben von der Konsole zu lesen. Die `input()` Funktion liest die Eingabe als Zeichenkette (String), und man kann sie dann in den gewünschten Datentyp konvertieren.\n", + "\n", + "Hier ist ein Beispiel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "name = input(\"Geben Sie Ihren Namen ein: \")\n", + "print(\"Hallo, \" + name + \". Willkommen in Python!\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dieses Programm fordert den Benutzer auf, seinen Namen einzugeben, liest die Eingabe als Zeichenkette und gibt dann eine personalisierte Nachricht aus. Beachte, dass die Eingabe innerhalb der `input()` Funktion in Klammern eingegeben wird.\n", + "\n", + "# Ausgabe in Python\n", + "Die einfachste Möglichkeit, in Python Ausgaben zu erstellen, besteht darin, die print()-Funktion zu verwenden. Diese Funktion druckt eine Zeichenkette oder eine Variable auf der Konsole." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "name = \"Max\"\n", + "age = 20\n", + "print(\"Mein Name ist\", name, \"und ich bin\", age, \"Jahre alt.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dieses Programm erstellt eine personalisierte Nachricht, indem es den Namen und das Alter des Benutzers verwendet und diese auf der Konsole ausgibt. Beachte, dass wir hier mehrere Argumente an die `print()` Funktion übergeben haben. Diese werden durch ein Komma getrennt und die Funktion fügt automatisch Leerzeichen zwischen den Argumenten hinzu." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.13" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/01_basics/015Strings.ipynb b/docs/01_basics/015Strings.ipynb new file mode 100644 index 0000000..8f43751 --- /dev/null +++ b/docs/01_basics/015Strings.ipynb @@ -0,0 +1,271 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Strings\n", + "\n", + "Strings können in Python mit einfachen oder doppelten Anführungszeichen initialisiert werden.\n", + "Im nachfolgenden Code findest du ein paar Beispiele:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random_string = \"Hello\"\n", + "random_string2 = 'World'\n", + "\n", + "random_multiline_string = \"\"\"Say\n", + "my\n", + "name!\"\"\"\n", + "\n", + "random_multiline_string2 = '''Hello\n", + "my name is\n", + "Jeff'''\n", + "\n", + "print(random_multiline_string2)\n", + "# Output:\n", + "# Hello\n", + "# my name is\n", + "# Jeff" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wie im obigen Code ersichtlich, ist es möglich, Strings über mehrere Zeilen mit dreifachen Anführungszeichen zu initialisieren.\n", + "Hierbei werden die Leerzeichen und Zeilenumschläge mitgespeichert. Ein String über 3 Zeilen wird bei einem `print()` Aufruf auch über 3 Zeilen in der Konsole ausgegeben." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Slicing\n", + "\n", + "Eine weitere coole Operation auf Strings ist das sogenannte **Slicing**.\n", + "\n", + "Wie der Name schon suggeriert, kann man Strings in verschiedene Teile zerlegen. Einige slicing Operationen sind:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "beispiel = \"Python ist eine tolle Programmiersprache!\"\n", + "\n", + "# Herausschneiden zwischen dem ersten und zweiten Index.\n", + "# Beachte, dass der Buchstabe mit dem 2. Index nicht inklusive ist.\n", + "print(beispiel[2:12]) # thon ist e\n", + "print(beispiel[2:4]) # th\n", + "\n", + "# Herausschneiden der ersten Buchstaben\n", + "print(beispiel[:5]) # Pytho\n", + "print(beispiel[:20]) # Python ist eine toll\n", + "\n", + "# Weglassen der ersten Buchstaben\n", + "print(beispiel[4:]) # on ist eine tolle Programmiersprache!\n", + "print(beispiel[22:]) # Programmiersprache!\n", + "\n", + "# Start beim 8. letzten Buchstaben, Ende beim 6. letzten Buchstaben.\n", + "print(beispiel[-8:-5]) # spr" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit `beispiel[2:12]` haben wir den String ab dem 3. bis zum 12. Buchstaben ausgeschnitten. Die erste Zahl ist der sogenannte Start-Index und die zweite der End-Index, wobei die 12 exklusiv ist (also gerade bis vor Index 12).\n", + "\n", + "Wie in fast allen anderen Programmiersprachen auch, beginnt man bei Index 0. Also das erste Element in Listen und Strings hat Index 0.\n", + "\n", + "Lässt du eine Zahl aus beim Slicing, dann wird der Default-Wert verwendet:\n", + "* Bei der ersten Zahl ist es 0: also von Beginn an.\n", + "* Bei der zweiten Zahl ist es -1: Also bis ganz nach hinten.\n", + "\n", + "Negative zahlen bedeuten, dass man von hinten beginnt zu rechnen:\n", + "* -1 ist das letzte Element.\n", + "* -2 das zweitletzte Element.\n", + "* usw." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du z.B. nur einen Buchstaben an einer bestimmten Position im String erhalten, dann kannst du via Index darauf zugreifen. Im folgenden Beispiel greifen wir auf den 2. Buchstaben (Index = 1) zu:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "beispiel[1]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## String-Methoden" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wie auch in Java hat Python einige vordefinierte Methoden auf Strings.\n", + "\n", + "Einige sind nachfolgend Aufgelistet:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s = \"Python is fun!\"\n", + "\n", + "print(s.upper()) # PYTHON IS FUN!\n", + "print(s.lower()) # python is fun!\n", + "\n", + "# Ersetzen eines Substrings mit einem anderen Substring\n", + "print(s.replace(\"n\", \"m\")) # Pythom is fum!\n", + "print(s.replace(\" \", \"#\")) # Python#is#fun!\n", + "print(s.replace(\"Python\", \"Java\")) # Java is fun!\n", + "\n", + "# .strip() entfernt alle Leerzeichen am Anfang und am Ende\n", + "print(\" to many whitespaces \".strip()) # to many whitespaces\n", + "\n", + "# .split() gibt eine Liste mit substrings zurück.\n", + "print(s.split(\" \")) # ['Python', 'is', 'fun!']\n", + "print(s.split(\"n\")) # ['Pytho', ' is fu', '!']\n", + "print(s.split(\"is\")) # ['Python ', ' fun!']\n", + "\n", + "# Strings können mit dem + Operator aneinandergeheftet werden.\n", + "print(s + \" Java too!\") # Python is fun! Java too!" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Einige der bekanntesten String-Methoden sind:\n", + "* `upper()`: Gibt den String in Grossbuchstaben zurück (verändert den ursprünglichen String aber nicht).\n", + "* `lower()`: Gibt den String in Kleinbuchstaben zurück.\n", + "* `replace()`: Gibt einen neuen String zurück, wobei bestimmte aufeinander folgende Buchstaben (Parameter 1) mit anderen Buchstaben (Parameter 2) ausgetauscht wurden.\n", + "* `strip()`: Gibt einen neuen String zurück ohne die Leerzeichen am Anfang und am Ende." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Falls du noch weitere Methoden sehen möchtest gibt es hier eine grosse Liste: https://www.w3schools.com/python/python_strings_methods.asp" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Escape Character\n", + "\n", + "Um Zeichen in einem String hinzufügen zu können, welche normalerweise nicht erlaubt sind, kann ein `\\` verwendet werden.\n", + "Es gibt auch weitere spezielle Zeichen, welche mit Hilfe eines Backslashes in einen String eingefügt werden können." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ' muss nicht escaped werden, \" aber schon.\n", + "print(\"Es gibt einfache (') und doppelte (\\\") Anführungszeichen\")\n", + "# Hier ist es genau umgekehrt\n", + "print('Es gibt einfache (\\') und doppelte (\") Anführungszeichen')\n", + "# Escapen des Escape characters\n", + "print(\"Ich verwende ein \\\\, um zu escapen\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du eine neue Zeile in einem String einfügen, dann kannst du das mit `\\n` (n steht für \"newline\") tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Hello, it's me.\\nHello from the other side\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In bestimmten Fällen möchtest du auch Tabulatoren hinzufügen (z.B. wenn du rasch einen Text mit mehreren Spalten generieren möchtest, denn du dann direkt in Excel einfügen kannst).\n", + "\n", + "Dies kannst du mit dem String `\\t` (t wie \"tab\") erreichen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"name \\t age \\t weight\")\n", + "print(\"Mario \\t 1 \\t 500\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/020Lists.py b/docs/020Lists.py deleted file mode 100644 index dad9f67..0000000 --- a/docs/020Lists.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -In Python gibt es keine Arrays, dafür Listen. -Eine Liste (engl "List") ist im Prinzip ein Array, benutzt auch die gleiche Syntax wie ein Array in Java, hat aber die Funktionalitäten einer ArrayList. -Wir sehen im folgenden Code-Beispiel wie mit Listen gearbeitet werden kann. -Da in Python alle Variablen als Objekte gespeichert sind, kann man in eine Liste auch verschiedene Datentypen einfügen. -Aber Vorsicht! Dies ist nicht zu empfehlen und Fehleranfällig! -Einige Funktionen, wie zum Beispiel das Sortieren funktioniert nicht bei einer Liste mit unterschiedlichen Datentypen! - -""" - -###################################################################### -my_list = [] # create an empty list -my_list.append(1) # add (int) 1 to the list -my_list.append(2) -my_list.append("Regenschirm") # add (str) "Regenschirm" to the list -print(my_list[0]) # Output: 1 -print(my_list[1]) # Output: 2 -print(my_list[2]) # Output: Regenschirm - -my_list2 = [1, 2, "Regenschirm"] # initialize the same list directly -my_list2.pop(0) # Remove element at pos 0 - -print(my_list2) # Output: [2, 'Regenschirm'] - -###################################################################### - -""" -In diesem Beispiel sieht man, dass das Printen einer Liste einfach mit print(list) machbar ist. Es braucht hierfür keine toString Methode wie z.B. in Java. -Hilfreiche Operationen bezüglich Listen sind die folgenden: -""" - -###################################################################### - -list_name = [] # leere Liste initialisieren -list_name = {"element1", "element2"} # Initialisierung mit Elementen -# Initialisierung mit Elementen via Konstruktor -list_name = list(("element1", "element2")) - -list_name.append("element") # fügt ein Element am Ende hinzu -# fügt das Element an der angegebenen Position hinzu -list_name.insert("position", "element") - -list_name.pop("position") # löscht das Element an der angegebenen Position -list_name.remove("element") # löscht das angegebene Element -list_name.clear() # löscht alle Elemente - -len(list_name) # gibt die Länge (anzahl Elemente) zurück -list_name.reverse() # dreht die Reihenfolge der Liste um -list_name.sort() # sortiert die Liste - -# gibt den Index des ersten elementes mit diesem Wert aus -list_name.index("element") -###################################################################### - - -""" -Zusatz: -Wir haben zuvor die Mehrfachzuweisung von Variablen kennengelernt. -Diese können wir nun anwenden, um Werte direkt aus einer Liste zu extrahieren und in Variablen zu speichern. -Siehe folgendes Beispiel: -""" - -###################################################################### - -numbers = [31, 5007, 19] -x, y, z = numbers -print(x) # 31 -print(y) # 5007 -print(z) # 19 - -###################################################################### diff --git a/docs/02_control_structures/040Conditions_and_if_else.ipynb b/docs/02_control_structures/040Conditions_and_if_else.ipynb new file mode 100644 index 0000000..4a9adc0 --- /dev/null +++ b/docs/02_control_structures/040Conditions_and_if_else.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Bedingungen\n", + "Wie im richtigen Leben müssen Entscheidungen entsprechend der aktuellen Situation getroffen werden.\n", + "\n", + "Hierfür bieten Programmiersprachen wie Python die Möglichkeit, Bedingungen zu prüfen und Code nur auszuführen, wenn die entsprechenden Bedingungen erfüllt sind." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nachfolgend ein Beispiel für einen kleinen Snack-Automaten, der je nach dem ob genügend Geld eingeworfen wurde unterschiedlichen Output generiert:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "money = 1.25\n", + "price = 1.50\n", + "\n", + "if money < price:\n", + " print(\"Du hast nicht genügend Geld eingeworfen. Bitte werfe zusätzlich\", price - money, \"Fr ein.\")\n", + "else:\n", + " print(\"Geniesse deinen Snack!\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hier wird eine Bedingung mit dem `if`-Keyword eingeleitet. Nur wenn die Bedingung nach dem `if`-Keyword (und vor dem Doppelpunkt) zutrifft, wird ihr Block ausgeführt.\n", + "\n", + "Wenn die Bedingung nicht zutrifft, dann wird der `else`-Block ausgeführt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bool'sche Ausdrücke" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Im Beispiel des Snackautomaten hast du den `<`-Operator verwendet, um zu prüfen, ob das eingeworfene Geld mind. dem Preis entspricht:\n", + "\n", + "```python\n", + "if money < price:\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Python gibt es viele weitere bool'sche Ausdrücke:\n", + "* `==`: Prüft, ob zwei Elemente \"gleich\" sind.\n", + "* `!=`: Prüft, ob die beiden Elemente ungleich sind.\n", + "* `<`: Prüft, ob das erste Element **kleiner** als das Zweite ist.\n", + "* `<=`: Prüft, ob das erste Element **kleiner oder gleich** als das Zweite ist.\n", + "* `>`: Prüft, ob das erste Element **grösser** als das Zweite ist.\n", + "* `>=`: Prüft, ob das erste Element **grösser oder gleich** als das Zweite ist." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Müssen hingegen mehrere Bedingungen erfüllt sein, dann kannst du mehrere Bedingungen kombinieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "money = 1.25\n", + "price = 1.50\n", + "currency = \"CHF\"\n", + "expected_currency = \"CHF\"\n", + "\n", + "if money >= price and currency == expected_currency:\n", + " print(\"Geniesse deinen Snack.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel wird der `if`-Block nur ausgeführt, wenn beide Bedingungen erfüllt sind." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Neben dem `and`-Operatoren gibt es in Python noch weitere Operatoren, die englische Wörter sind:\n", + "\n", + "* `and` prüft, ob beide Bedingungen zutreffen.\n", + "* `or` prüft, ob mind. 1 der Bedingungen zutreffen.\n", + "* `in` prüft, ob ein Element in einer Sequenz (z.B. einer Liste) vorhanden ist.\n", + "* `is` im Gegensatz zum `==` vergleicht der is-Operator nicht die Werte der Variablen, sondern die Instanzen selbst. Siehe Beispiel unten.\n", + "* `not` negiert einen bool'schen Ausdruck. Das heisst, tritt ein Ausdruck zu, trifft er nicht mehr zu und umgekehrt.\n", + "\n", + "Im folgenden Beispiel werden alle `print`-Blöcke ausgeführt, weil alle `if`-Bedingungen zutreffen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = True\n", + "b = False\n", + "\n", + "x = [1, 2, 3]\n", + "y = [1, 2, 3]\n", + "\n", + "print(x == y) # Output: True\n", + "print(x is y) # Output: False\n", + "\n", + "if a and b == False:\n", + " print(\"a is True and b is False.\")\n", + "\n", + "if a or b:\n", + " print(\"Either a, b or both of them are True.\")\n", + "\n", + "if 1 in x:\n", + " print(\"1 is in x.\")\n", + "\n", + "if a is not None: # Null-Check in Python.\n", + " print(\"a is not None.\")\n", + "\n", + "if not b:\n", + " print(\"b is false.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Komplexere Bedingungen mit `if`, `elif` und `else`\n", + "\n", + "Die Keywords `if`, `elif` und `else` werden für Bedingungen verwendet.\n", + "Hier ist ein kleines Beispiel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "age = 25\n", + "\n", + "if age < 18:\n", + " print(\"Du bist minderjährig.\")\n", + "elif age >= 18 and age < 65:\n", + " print(\"Du bist erwachsen.\")\n", + "else:\n", + " print(\"Du bist im Rentenalter.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wenn die vorherige Bedingung nicht zutraf, wird die Bedingung im `elif` geprüft. Wenn diese zutrifft, wird ihr Block ausgeführt.\n", + "\n", + "Wenn keine der Bedingungen zutrifft, wird der `else`-Block ausgeführt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Einen kleinen Hinweis noch an dieser Stelle: \n", + "\n", + "Das Schlüsselwort `pass` in Python wird verwendet, um einen leeren Block zu erstellen, der später mit Code gefüllt werden kann. Es ist nützlich, wenn eine Funktion oder eine Schleife syntaktisch korrekt sein muss, aber noch keinen Code benötigt. Ein Beispiel für die Verwendung von `pass` ist innerhalb einer if-Anweisung, wenn man noch nicht sicher ist, welcher Code ausgeführt werden soll, aber das Programm syntaktisch korrekt sein muss:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 5\n", + "\n", + "if x < 0:\n", + " print(\"x ist negativ.\")\n", + "elif x == 0:\n", + " pass\n", + "else:\n", + " print(\"x ist positiv.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel wird geprüft, ob x negativ, `0` oder positiv ist. Wenn x `0` ist, wird `pass` ausgeführt -> es passiert nichts.\n", + "\n", + "Wenn x negativ oder positiv ist, wird eine entsprechende Ausgabe erstellt.\n", + "\n", + "Häufig wird auch `pass` verwendet, wenn man eine Funktion oder Klasse definiert, aber noch nicht weiss, welchen Code Sie später darin befinden wird bzw. leer bleiben wird:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def empty_function():\n", + " pass\n", + "\n", + "class EmptyClass:\n", + " pass" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Theorie zu Klassen und Funktionen wirst du später kennenlernen." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/02_control_structures/050Loops.ipynb b/docs/02_control_structures/050Loops.ipynb new file mode 100644 index 0000000..dd4039b --- /dev/null +++ b/docs/02_control_structures/050Loops.ipynb @@ -0,0 +1,216 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Schlaufen/Loops\n", + "Loops erlauben es, Code-Blöcke mehrere Male nacheinander auszuführen. Nachfolgend schauen wir uns den `for`- und `while`-Loop an." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## For-Loop\n", + "\n", + "Es gibt mehrere Möglichkeiten, einen For-Loop in Python zu implementieren. \n", + "\n", + "* Eine Möglichkeit ist die Benutzung der `range()`-Funktion, welche sehr verschieden eingesetzt werden kann.\n", + "* Eine andere Möglichkeit ist es, über alle Elemente einer Liste (oder auch eines Sets, Dictionarys,) zu iterieren. (Wird in einem späteren Kapitel behandelt.)\n", + "\n", + "Beachte den Doppelpunkt und die Einrückung des Blockes.\n", + "\n", + "Das Nachfolgende Beispiel zeigt einige `for`-Loops:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(10): # i = 0, 1, 2, ... , 9\n", + " print(i)\n", + "\n", + "for i in range(3, 8): # i = 3, 4, 5, 6, 7\n", + " print(i)\n", + "\n", + "\n", + "for i in range(0, 10, 3): # i = 0, 3, 6, 9\n", + " print(i)\n", + "\n", + "numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", + "for number in numbers: # prints out every number in the numbers list\n", + " print(number)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Erklärung zum obigen Beispiel**\n", + "* Die `range(...)`-Funktion gibt ein Konstrukt zurück, dass alle Elemente beinhaltet bis zu einer bestimmten Zahl. \n", + " * `range(end)`: `range(10)` beinhaltet alle Zahlen von 0 bis 9.\n", + " * `range(start, end)`: `range(3,8)` beinhaltet so alle Zahlen von 3 bis 7. \n", + " * `range(start, end, step)`: Ein drittes Argument bewirkt, dass z.B. nur jedes 3. Element berücksichtigt wird. `range(0, 10, 3)` geht von 0 bis 9 und nur jedes 3. Element (einschliesslich das erste) wird berücksichtigt. Wird aber nur selten gebraucht.\n", + "\n", + "* Beim letzten `for`-Loop siehst du besser, um was es bei `for`-Loops eigentlich geht: Man iteriert über alle Elemente in einer Liste.\n", + " * Listen werden in einem späteren Kapitel erwähnt. Daher musst du dieses Beispiel auch noch nicht zu 100% verstehen.\n", + " * Wichtig ist, dass die Variable `numbers` hier eine Liste mit einigen Elementen zugewiesen bekommen hat.\n", + " * Ein `for`-Loop ist ganz generell so aufgebaut:\n", + " ```python\n", + " for (ein_element in liste):\n", + " block()\n", + " ```\n", + " * Hier steht `ein_element` für eine neue Variable, die du definierst.\n", + " * Die `liste` ist eine existierende Variable, die mehrere Elemente enthält.\n", + " * Der `block` wird so oft ausgeführt, wie `liste` Elemente enthält.\n", + " * Beim ersten Durchgang wird deiner neuen Variable (hier `ein_element`) das erste Element aus der `liste` zugewiesen.\n", + " * Beim zweiten Durchgang das zweite Element.\n", + " * usw." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Beispiel: Iterieren durch einen String\n", + "\n", + "Du kannst `for`-Loops z.B. dafür verwenden, um durch alle Buchstaben in einem String zu iterieren:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = \"Hello World!\"\n", + "\n", + "for x in a:\n", + " print(x)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## While-Loop\n", + "\n", + "Wenn ein bestimmter Code solange wiederholt ausgeführt werden soll, wie eine Bedingung zutrifft, dann hilft hier womöglich auch eine `while`-Schlaufe." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du z.B. ein kleines Game simulieren, in welchem eine Katze versucht eine Maus zu fangen, dann könnte folgendes Konstrukt in die richtige Richtung führen:\n", + "\n", + "```python\n", + "caught = False\n", + "\n", + "while not caught:\n", + " caught = try_to_catch_mouse()\n", + "\n", + "print(\"Du hast die Maus gefangen. Gut gemacht!!!\")\n", + "```\n", + "\n", + "Der Code-Block innerhalb der `while`-Schlaufe wird solange wiederholt ausgeführt, wie die Bedingung (hier `not caught`) erfüllt ist." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In vielen Fällen kann ein `while`-Loop auch in einen `for`-Loop umgewandelt werden.\n", + "\n", + "Betrachte folgenden `for`-Loop:\n", + "\n", + "```python\n", + "for i in range(3):\n", + " print(i)\n", + "```\n", + "\n", + "Dieser `for`-Loop würde mit einer `while`-Schlaufe wie folgt aussehen (hier ist der `for`-Loop zu bevorzugen):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "i = 0\n", + "while i < 3:\n", + " print(i)\n", + " i += 1" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Schlaufe/Durchlauf abbrechen" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit den Statements `break` and `continue` kannst du die Schlaufe bzw. den aktuellen Durchlauf abbrechen:\n", + "* `continue` beendet den aktuellen Durchlauf und geht zum nächsten Schlaufen-Durchlauf weiter.\n", + "* `break` beendet die Schlaufe komplett." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "i = 0\n", + "while True:\n", + " i += 1\n", + " if i == 2:\n", + " continue\n", + " if i >= 5:\n", + " break\n", + " print(i)\n", + "\n", + "#output: 1, 3, 4" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/02_control_structures/060Functions.ipynb b/docs/02_control_structures/060Functions.ipynb new file mode 100644 index 0000000..53a7c44 --- /dev/null +++ b/docs/02_control_structures/060Functions.ipynb @@ -0,0 +1,223 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Funktionen\n", + "\n", + "Es kommt ziemlich oft vor, dass der gleiche Code bzw. die gleiche Aufgabe mehrmals mit anderen Werten aufgerufen werden soll.\n", + "\n", + "Solcher Code kann wiederverwendet werden, indem er in eine Funktion gepackt wird." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Funktionen werden mit dem Keyword `def` definiert. Eine Funktion ist in Python folgendermassen aufgebaut:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def funktions_name(arg1, arg2):\n", + " # Code, der aufgerufen wird.\n", + " pass" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hier zwei Beispiele von Funktionen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def print_hello():\n", + " print(\"Hello\")\n", + "\n", + "\n", + "def sum(a, b):\n", + " return a + b\n", + "\n", + "\n", + "# Call the defined functions\n", + "print_hello()\n", + "print_hello()\n", + "print(sum(1, 2))\n", + "print(sum(1, 6))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die erste Funktion kann mit der Anweisung `print_hello()` aufgerufen werden und führt den Code in der Funktion aus.\n", + "\n", + "Die zweite Funktion schauen wir uns nachfolgend noch ein bisschen genauer an." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Funktionen mit Parameter und Rückgabewert\n", + "\n", + "### Parameter\n", + "Im letzten Beispiel hatten wir diese Funktion:\n", + "\n", + "```python\n", + "def sum(a, b):\n", + " return a + b\n", + "```\n", + "\n", + "Das in den Klammern nennt sich \"Parameter\". Die einzelnen Variablen dort drin (also `a` und `b`) nennen sich Argumente.\n", + "\n", + "In unserem letzten Code-Beispiel sah der Funktionsaufruf wie folgt aus, wobei auch gleich Werte für den Parameter übergeben wurden:\n", + "\n", + "```python\n", + "sum(1, 6)\n", + "```\n", + "\n", + "Für diesen Funktionsaufruf werden dann die Argumente der Funktion wie folgt zugewiesen sein:\n", + "\n", + "```python\n", + "a = 1\n", + "b = 6\n", + "```\n", + "\n", + "### Rückgabewert\n", + "Sicherlich ist dir bei dieser Funktion das Keyword `return` aufgefallen:\n", + "\n", + "```python\n", + "def sum(a, b):\n", + " return a + b\n", + "```\n", + "\n", + "Dieses `return`-Keyword ermöglicht es, dem Aufrufer etwas zurückzugeben. Siehe dir dieses Beispiel an:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def sum(a, b):\n", + " return a + b\n", + "\n", + "result = sum(5, 6)\n", + "print(result)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Entscheidend ist diese Zeile:\n", + "\n", + "```python\n", + "result = sum(5, 6)\n", + "```\n", + "\n", + "Wir können z.B. einer Variable den Rückgabewert einer Funktion zuweisen. \n", + "\n", + "Das `return`-Keyword gibt den Wert zurück, der rechts neben ihm steht und bricht die weitere Ausführung der Funktion ab." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Funktionen dokumentieren\n", + "\n", + "Es können noch einige zusätzliche Informationen bei der Definition angegeben werden wie beispielsweise\n", + "* ein DocString (Beschreibung der Funktion),\n", + "* der `return`-Typ\n", + "* sowie die Typen der Argumente.\n", + "\n", + "Die Funktionen von oben könnten also wie folgt dokumentiert werden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def print_hello() -> None:\n", + " \"\"\"Prints 'Hello' into the console\"\"\"\n", + " print(\"Hello\")\n", + "\n", + "\n", + "def sum(i: int, j: int) -> int:\n", + " \"\"\"\n", + " Returns the sum of two numbers.\n", + " \n", + " Parameters:\n", + " i (int): the first number to be added\n", + " j (int): the second number to be added\n", + "\n", + " Returns:\n", + " int: the sum of i and j\n", + " \"\"\"\n", + " return i + j" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dies ist aber nur eine Information für die Entwickler und hat keinen Einfluss auf das Programm!\n", + "\n", + "Wenn wir die Funktion `sum` z.B. als `sum(1, \"green\")` aufrufen würden, dann würde das Programm zur Laufzeit einen Fehler werfen!\n", + "Grund hierfür ist, dass diese Funktion nur mit Zahlen umgehen kann.\n", + "\n", + "Daher ist es von Vorteil, DocStrings sowie Argumente und Rückgabetypen anzugeben, um während des Schreibens von Code mögliche Fehler besser zu erkennen.\n", + "\n", + "In vielen Entwicklungumgebungen wie VS Code oder PyCharm sind diese diese Informationen ersichtlich, während du eine Methode auswählst oder mit der Maus über sie fährst.\n", + "\n", + "Eine Liste mit Datentypen in Python gibt es hier: https://www.w3schools.com/python/python_datatypes.asp" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/030Basic_Operators.py b/docs/030Basic_Operators.py deleted file mode 100644 index 2756c04..0000000 --- a/docs/030Basic_Operators.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -In diesem Abschnitt thematisieren wir grundlegende Operanden in Python. -Die +, -, *, / und % (modulo) Operanden auf Zahlen verhalten sich wie in Java. Es gilt ebenfalls Punkt vor Strich etc. -Es gibt zusätzlich den ** Operanden, welcher für das Potenzieren benutzt werden kann: -""" - -###################################################################### - -print(1 + 2 * 3 / 4) # Output: 2.5 -print(10 % 3) # Output: 1 - - -num1 = 3 ** 2 # = 3 * 3 = 9 -num2 = 2 ** 8 # = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 = 2^8 = 256 -print(num1) # 9 -print(num2) # 256 - -###################################################################### - -""" -Python unterstützt auch das Multiplizieren von Zeichenfolgen, um eine Zeichenfolge mit einer sich wiederholenden Sequenz zu bilden: -""" - -###################################################################### - -string1 = "bla" * 4 -print(string1) # Output: blablablaba - -###################################################################### - -""" -Dasselbe Prinzip funktioniert auch bei Listen : -""" - -###################################################################### - -my_list = [0, 100, 5000] * 3 -print(my_list) # Output: [0, 100, 5000, 0, 100, 5000, 0, 100, 5000] - -###################################################################### - - -""" -Mit dem + Operator können Listen vereinigt werden. Hierbei werden alle Elemente der zweiten Liste am Ende der ersten Liste hinzugefügt. -""" - -###################################################################### - -numbers = [0, 1, 2, 3] -large_numbers = [1001, 2000] -colors = ["red", "blue", "yellow"] - -big_list = numbers + large_numbers + colors - -# Output: [0, 1, 2, 3, 1001, 2000, 'red', 'blue', 'yellow'] -print(big_list) - -###################################################################### - -""" -Hier findest du eine Liste mit vielen Pthon Operatoren: https://www.w3schools.com/python/python_operators.asp -""" diff --git a/docs/03_collections/020Lists.ipynb b/docs/03_collections/020Lists.ipynb new file mode 100644 index 0000000..35cbe4e --- /dev/null +++ b/docs/03_collections/020Lists.ipynb @@ -0,0 +1,206 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Listen\n", + "\n", + "In Python gibt es keine Arrays, dafür Listen. \n", + "Eine Liste (engl \"List\") benutzt eine ähnliche Syntax wie ein Array in Java, hat aber die Funktionalitäten einer `List`.\n", + "\n", + "Wir sehen im folgenden Code-Beispiel wie mit Listen gearbeitet werden kann.\n", + "\n", + "Da in Python alle Variablen als Objekte gespeichert sind, kann man in einer Liste auch Objekte verschiedener Datentypen einfügen.\n", + "Aber Vorsicht! Dies ist nicht zu empfehlen, weil es fehleranfällig ist!\n", + "Einige Funktionen, wie zum Beispiel das Sortieren funktioniert nicht bei einer Liste mit unterschiedlichen Datentypen!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_list = [] # create an empty list\n", + "my_list.append(1) # add (int) 1 to the list\n", + "my_list.append(2)\n", + "my_list.append(\"Regenschirm\") # add (str) \"Regenschirm\" to the list\n", + "print(my_list[0]) # Output: 1\n", + "print(my_list[1]) # Output: 2\n", + "print(my_list[2]) # Output: Regenschirm\n", + "\n", + "my_list2 = [1, 2, \"Regenschirm\"] # initialize the same list directly\n", + "my_list2.pop(0) # Remove element at pos 0\n", + "\n", + "print(my_list2) # Output: [2, 'Regenschirm']" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit dieser Anweisung haben wir eine neue, leere Liste erstellt:\n", + "```python\n", + "my_list = []\n", + "```\n", + "\n", + "* Mit der Funktion `append(...)` werden neue Elemente in der Liste hinzugefügt.\n", + "* Mit `pop(...)` wird das Element an der angegebenen Position entfernt (wobei das erste Element an Position 0, das zweite an Position 1 usw. ist)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Durch Listen iterieren\n", + "\n", + "Das, was du am häufigsten mit einer Liste machen wirst, ist wahrscheinlich, durch alle ihre Elemente zu iterieren. Das kannst du am besten mit einer `for`-Schleife:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for element in my_list:\n", + " print(element)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel wurde jedes einzelne Element in mit der `print(...)`-Funktion in der Konsole ausgegeben." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Weitere Funktionen auf Listen" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hilfreiche Operationen bezüglich Listen sind die folgenden:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "list_name = [] # leere Liste initialisieren\n", + "list_name = [\"element1\", \"element2\"] # Initialisierung mit Elementen\n", + "# Initialisierung mit Elementen via Konstruktor\n", + "list_name = list((\"element1\", \"element2\"))\n", + "\n", + "list_name.append(\"element3\") # fügt ein Element am Ende hinzu\n", + "# fügt das Element an der angegebenen Position hinzu\n", + "list_name.insert(2, \"element4\")\n", + "\n", + "list_name.pop(2) # löscht das Element an der angegebenen Position aus der Liste und gibt diesen Element zurück.\n", + "index_of_element1 = list_name.index(\"element1\")\n", + "list_name.remove(\"element1\") # löscht das erste Element mit dem angegebenen Wert.\n", + "\n", + "contains_element2: bool = \"element2\" in list_name # Ist True, wenn das Element in der Liste vorkommt.\n", + "\n", + "len(list_name) # gibt die Länge (anzahl Elemente) zurück\n", + "list_name.reverse() # dreht die Reihenfolge der Liste um\n", + "list_name.sort() # sortiert die Liste" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Eine Liste kannst du auf diese Arten neu erstellen:\n", + "* direkt mit der Listen-Schreibweise:\n", + "```python\n", + "list_name = []\n", + "list_name = [\"element1\", \"element2\"] \n", + "```\n", + "* oder mit der Funktion `list`:\n", + "```python\n", + "list_name = list()\n", + "list_name = list((\"element1\", \"element2\"))\n", + "```\n", + "\n", + "Neue Elemente können mit `append` und `insert` hinzugefügt werden.\n", + "* `append(element)` fügt ein neues Element am Ende der Liste hinzu.\n", + "* `insert(position: int, element)` fügt das `element` an der angegebenen `position` hinzu.\n", + "\n", + "Mit `index(element)` erhältst du den Index/die Position des Elementes, an der es zum ersten Mal in der Liste vorkommt.\n", + "\n", + "Elemente löschen kannst du mit `pop(index)` und `remove(element)`.\n", + "* `pop(index)` löscht das Element an der angegebenen Position.\n", + "* `remove(element)` löscht das erste Element mit dem angegebenen Wert aus der Liste.\n", + "\n", + "Mit dem `in`-Operator kannst du prüfen, ob ein bestimmtes Element in der Liste vorkommt. Ist das Gleiche wie `contains()` oder `includes()` in anderen Programmiersprachen. \n", + "\n", + "Mit `len(liste)` erhältst du die Anzahl Elemente in der Liste.\n", + "\n", + "Sortieren kannst du die Liste mit `sort()` und `reverse()`." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mehrfachzuweisungen aufgrund einer Liste \n", + "Wir haben zuvor die Mehrfachzuweisung von Variablen kennengelernt. \n", + "Diese können wir nun anwenden, um Werte direkt aus einer Liste zu extrahieren und in Variablen zu speichern.\n", + "\n", + "Siehe folgendes Beispiel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [31, 5007, 19]\n", + "x, y, z = numbers\n", + "print(x) # 31\n", + "print(y) # 5007\n", + "print(z) # 19" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/03_collections/021_Basic_Operstors.ipynb b/docs/03_collections/021_Basic_Operstors.ipynb new file mode 100644 index 0000000..989b526 --- /dev/null +++ b/docs/03_collections/021_Basic_Operstors.ipynb @@ -0,0 +1,120 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Weitere Operationen\n", + "\n", + "Wir schauen uns hier noch einmal ein paar Operationen an, die Python speziell anbietet. Ziel dieses Kapitels ist es zu verstehen, dass in Python Operanden grundsätzlich auf alle Datentypen möglich sind." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zahlen\n", + "Im nächsten Code-Listing thematisieren wir grundlegende Zahlen-Operanden in Python.\n", + "\n", + "Die `+`, `-`, `*`, `/` und `%` (modulo)-Operanden auf Zahlen verhalten sich wie in Java. Es gilt ebenfalls Punkt vor Strich etc.\n", + "Es gibt zusätzlich den `**` Operanden, welcher für das Potenzieren benutzt werden kann:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(1 + 2 * 3 / 4) # Output: 2.5\n", + "print(10 % 3) # Modulo-Operation, sprich 3er-Rest. # Output: 1\n", + "\n", + "\n", + "num1 = 3 ** 2 # = 3 * 3 = 3^2 = 9\n", + "num2 = 2 ** 8 # = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 = 2^8 = 256\n", + "print(num1) # 9\n", + "print(num2) # 256" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Strings\n", + "Python unterstützt das Multiplizieren von Zeichenfolgen, um eine Zeichenfolge mit einer sich wiederholenden Sequenz zu bilden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "string1 = \"bla\" * 4\n", + "print(string1) # Output: blablablaba" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Listen\n", + "Dasselbe Prinzip funktioniert auch bei Listen :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_list = [0, 100, 5000] * 3\n", + "print(my_list) # Output: [0, 100, 5000, 0, 100, 5000, 0, 100, 5000]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit dem `+` Operator können Listen vereinigt werden. Hierbei werden alle Elemente der zweiten Liste am Ende der ersten Liste hinzugefügt." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [0, 1, 2, 3]\n", + "large_numbers = [1001, 2000]\n", + "colors = [\"red\", \"blue\", \"yellow\"]\n", + "\n", + "big_list = numbers + large_numbers + colors\n", + "\n", + "# Output: [0, 1, 2, 3, 1001, 2000, 'red', 'blue', 'yellow']\n", + "print(big_list)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hier findest du eine Liste mit vielen Python Operatoren: https://www.w3schools.com/python/python_operators.asp" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/03_collections/022_Listen_Komprehensionen.ipynb b/docs/03_collections/022_Listen_Komprehensionen.ipynb new file mode 100644 index 0000000..34dee37 --- /dev/null +++ b/docs/03_collections/022_Listen_Komprehensionen.ipynb @@ -0,0 +1,69 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Listen-Komprehensionen\n", + "Listen-Komprehensionen in Python sind eine Möglichkeit, um neue Listen auf Basis von existierenden iterierbaren Objekten zu erstellen. Sie bestehen aus einer einzigen Codezeile, welche die Operation angibt, die auf jedem Element des iterierbaren Objekts durchgeführt werden soll. Die resultierende Liste wird on-the-fly generiert und als einzelner Ausdruck zurückgegeben.\n", + "\n", + "Im Allgemeinen hat eine Listen-Komprehension die folgende Syntax:\n", + "\n", + "```python\n", + "new_list = [expression for variable in iterable if condition]\n", + "```\n", + "\n", + "Hierbei ist `expression` ein beliebiger, gültiger Python-Ausdruck, welcher einen Wert produziert. `variable` ist der Name der Schleifenvariable, welche jeden Wert in `iterable` annimmt. `iterable` ist ein iterierbares Objekt (z.B. eine Liste, ein Tuple, ein Range-Objekt), welches die Werte für variable generiert. Mit einer (optionalen) `condition` können wir Elemente überspringen, die nicht dieser `condition` entsprechen.\n", + "\n", + "Die Listen-Komprehension erzeugt dann eine neue Liste (hier in der Variable `new_list` gespeichert), indem der `expression` auf jeden Wert von `variable` in `iterable` angewandt wird.\n", + "\n", + "Zum Beispiel generiert die Listen-Komprehension `[i * 2 for i in range(5)]` die Liste `[0, 2, 4, 6, 8]`, indem sie den Ausdruck `i * 2` auf jeden Wert von `i` im Bereich von 0 bis 4 anwendet.\n", + "\n", + "Hier sind noch ein paar weitere Beispiele von Listen-Komprehensionen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Quadrate von Zahlen von 1 bis 10\n", + "quadrat = [i ** 2 for i in range(1, 11)]\n", + "print(quadrat) # Ausgabe: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\n", + "\n", + "# Gerade Zahlen von 0 bis 20\n", + "gerade_zahlen = [i for i in range(21) if i % 2 == 0]\n", + "print(gerade_zahlen) # Ausgabe: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]\n", + "\n", + "# Liste von Tupeln\n", + "woerter = [\"Apfel\", \"Banane\", \"Kirsche\"]\n", + "tupel = [(wort, len(wort)) for wort in woerter]\n", + "print(tupel) # Ausgabe: [('Apfel', 5), ('Banane', 6), ('Kirsche', 6)]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/03_collections/025Sets.ipynb b/docs/03_collections/025Sets.ipynb new file mode 100644 index 0000000..5c3dc2c --- /dev/null +++ b/docs/03_collections/025Sets.ipynb @@ -0,0 +1,172 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sets\n", + "\n", + "Python-Sets sind eine unglaublich nützliche Datenstruktur, mit der man ungeordnete, eindeutige Elemente speichern kann. Sie ähneln Listen und Tupeln, aber Sets können keine doppelten Werte enthalten. In dieser Einführung werden wir die Grundlagen zum Erstellen und Verwenden von Sets sowie einige gängige Set-Operationen behandeln.\n", + "\n", + "# Ein Set erstellen\n", + "Es gibt zwei Möglichkeiten, ein Set in Python zu erstellen: Mit der Funktion `set()` oder mit geschweiften Klammern `{}`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Creating an empty set\n", + "empty_set = set()\n", + "print(empty_set) # Output: set()\n", + "\n", + "# Creating a set using curly braces\n", + "fruits = {\"apple\", \"banana\", \"orange\"}\n", + "print(fruits) # Output: {'apple', 'banana', 'orange'}\n", + "\n", + "# Creating a set from a list using the set() function\n", + "numbers_list = [1, 2, 2, 3, 4, 4]\n", + "unique_numbers = set(numbers_list)\n", + "print(unique_numbers) # Output: {1, 2, 3, 4}\n", + "\n", + "# A set does not contain dublicates\n", + "color_set = {\"green\", \"blue\", \"red\", \"green\"}\n", + "print(color_set) # Output: {'red', 'blue', 'green'}\n", + "\n", + "# The values True and 1 are considered the same value in sets, and are treated as duplicates:\n", + "# Same goes for 0 and False\n", + "test_set = {\"hello\", True, 1, 2, 3, False, 0}\n", + "print(test_set) # Output: {False, True, 2, 3, 'hello'}\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## List in Set umwandeln\n", + "\n", + "Hast du eine Liste, und du möchtest nun wissen, wie viele verschiedene Elemente diese besitzt (also du willst keine Duplikate), dann bietet Python mit `set(...)` eine sehr praktische Funktion an:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_list = [1,2,3,4,5,1,1,4,3,99]\n", + "\n", + "values = set(my_list)\n", + "\n", + "print(\"All values in the list:\", values)\n", + "print(f\"The list contains {len(values)} different values.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Grundlegende Set-Operationen\n", + "Sets haben integrierte Methoden, mit denen Sie verschiedene Operationen ausführen können, wie das Hinzufügen von Elementen, das Entfernen von Elementen und das Überprüfen, ob ein Element in einem Set enthalten ist." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fruits = {\"apple\", \"banana\", \"orange\"}\n", + "\n", + "# Adding an element to a set\n", + "fruits.add(\"grape\")\n", + "print(fruits) # Output: {'apple', 'banana', 'orange', 'grape'}\n", + "\n", + "# Removing an element from a set\n", + "fruits.remove(\"banana\")\n", + "print(fruits) # Output: {'apple', 'orange', 'grape'}\n", + "\n", + "# Checking if an element is in a set\n", + "print(\"apple\" in fruits) # Output: True\n", + "\n", + "# Getting the length of a set\n", + "print(len(fruits)) # Output: 3\n", + "\n", + "# Looping over all elements of a set\n", + "for x in fruits:\n", + " print(x)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Häufige Set-Operationen\n", + "Python-Sets unterstützen auch häufige mathematische Set-Operationen wie Vereinigung, Schnittmenge, Differenz und symmetrische Differenz." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Two sets of numbers:\n", + "even_numbers = {98, 1034, 32, 2, 4, 6, 8, 10}\n", + "odd_numbers = {1, 3, 5, 7, 9}\n", + "\n", + "# Union of two sets:\n", + "all_numbers = even_numbers.union(odd_numbers)\n", + "print(all_numbers) # Output: {32, 1, 2, 98, 4, 3, 6, 5, 8, 7, 1034, 10, 9}\n", + "\n", + "# Intersection of two sets:\n", + "common_numbers = {1, 2, 3, 4}.intersection({3, 4, 5, 6})\n", + "print(common_numbers) # Output: {3, 4}\n", + "\n", + "# Difference of two sets (nur das, was im ersten (links der Methode) Set vorhanden ist und im zweiten (parameter der Methode) nicht):\n", + "diff_numbers = {1, 2, 3, 4}.difference({3, 4, 5, 6})\n", + "print(diff_numbers) # Output: {1, 2}\n", + "\n", + "# Symmetric difference of two sets:\n", + "sym_diff_numbers = {1, 2, 3, 4}.symmetric_difference({3, 4, 5, 6})\n", + "print(sym_diff_numbers) # Output: {1, 2, 5, 6}" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Weitere Methoden auf Sets findest du hier: https://www.w3schools.com/python/python_sets_methods.asp" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/03_collections/027Tuples.ipynb b/docs/03_collections/027Tuples.ipynb new file mode 100644 index 0000000..1a1ebc6 --- /dev/null +++ b/docs/03_collections/027Tuples.ipynb @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tupel\n", + "\n", + "Ein Tupel ist ein Datencontainer in Python, der dazu verwendet wird, eine Sammlung von Werten zu speichern. Ein Tupel kann mehrere Werte enthalten, die nicht geändert werden können. Das bedeutet, dass man den Inhalt des Tupels nicht nach der Erstellung ändern kann.\n", + "\n", + "Tuples werden oft verwendet, wenn eine Funktion mehrere Rückgabewerte hat:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_names() -> tuple[str, str]:\n", + " return (\"first name\", \"last name\")\n", + "\n", + "first_name, last_name = get_names()\n", + "(first_name, last_name) = get_names()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Im obigen Code gibt die Funktion einen Vor- und einen Nachnamen zurück.\n", + "\n", + "In den unteren beiden Zeilen werden die Werte gleich in entsprechende Variablen kopiert. Beide Schreibweisen machen das Gleiche." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Man kann ein Tupel in Python mit runden Klammern erstellen. Hier sind einige Beispiele für Tupel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_tuple = (1, 2)\n", + "my_tuple2 = (1, 1, 2) # Dublikate sind erlaubt\n", + "my_tuple2 = (\"Python\", 3.9, True) # Ein Tupel kann verschiedene Datentypen beinhalten\n", + "my_tuple3 = ((1, 2), \"hello\") # Verschachtelung von Tupeln\n", + "empty_tuple = () # Leeres Tupel\n", + "my_tuple4 = (\"hallo\",) # Tupel mit nur einem Element\n", + "my_tuple5 = (\"hallo\") # KEIN Tupel! Dies ist ein String!" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Man kann auf die Werte in einem Tupel zugreifen, indem man den Index des Werts in eckigen Klammern nach dem Namen des Tupels schreibt. Beachte, dass der Index bei 0 beginnt. Hier ist ein Beispiel für den Zugriff auf den ersten Wert in einem Tupel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_tuple = (1, 2, 3)\n", + "print(my_tuple[0])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dies gibt den Wert 1 aus, da der erste Wert in einem Tupel den Index 0 hat.\n", + "\n", + "Da Tupel unveränderlich sind, kann man keine Werte hinzufügen, entfernen oder ändern. Wenn man jedoch ein neues Tupel mit geänderten Werten erstellen möchte, kann man dies tun, indem man die vorhandenen Werte aus dem Tupel kopiert und ändert. Hier ist ein Beispiel für die Erstellung eines neuen Tupels mit geänderten Werten:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_tuple = (1, 2, 3)\n", + "new_tuple = my_tuple[:2] + (4,) # Erstellt ein neues Tupel mit den ersten beiden Werten von my_tuple und dem Wert 4.\n", + "print(new_tuple)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dies gibt das Tupel (1, 2, 4) aus.\n", + "\n", + "Natürlich können auch Funktionen Tupel als Argumente nehmen oder zurückgeben. Ebenfalls kann man mit for-schleifen über Tupel iterieren. Es gibt ausserdem einige von Python zur Verfügung gestellte Funktionen auf Tupeln wie zum Beispiel `len()`, welche die Länge eines Tupels zurückgibt. Einige weitere Code Beispiele sind:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Verwendung von Tupeln als Dict Keys\n", + "my_dict = {(\"Alice\", 25): \"alice@example.com\", (\"Bob\", 30): \"bob@example.com\"}\n", + "print(my_dict[(\"Alice\", 25)]) # Gibt die E-Mail-Adresse von Alice aus (\"alice@example.com\")\n", + "\n", + "# Funktion, welche ein Tupel zurückgibt\n", + "def get_name_and_age():\n", + " name = \"Alice\"\n", + " age = 25\n", + " return name, age\n", + "\n", + "result = get_name_and_age()\n", + "print(result[0]) # Gibt den Namen aus (\"Alice\")\n", + "print(result[1]) # Gibt das Alter aus (25)\n", + "\n", + "# For schleife über ein Tupel\n", + "my_tuple = (1, 2, 3, 4)\n", + "for item in my_tuple:\n", + " print(item)\n", + "\n", + "# Verwendung der len() Funktion.\n", + "my_tuple = (1, 2, 3, 4)\n", + "print(len(my_tuple)) # Gibt 4 aus" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuples vs. Lists\n", + "\n", + "Wir haben gesehen, dass Tuples den Listen sehr ähneln. Wann sollen wir welchen Typ verwenden?\n", + "\n", + "| | Listen | Tuples |\n", + "| -- | --- | --- |\n", + "| Verwendung | In Listen werden oft sehr unterschiedlich viele Elemente gespeichert. | Tuples werden oft gebraucht, wenn eine Funktion mehrere Werte zurück gibt. Einzelne Variablen werden dann meistens aus dem Tuple in neue Variablen entpackt. | \n", + "| Immutable Data | Listen sind veränderbar. | Tuples sind unveränderbar, es können keine Elemente ausgetauscht, hinzugefügt oder gelöscht werden.|\n", + "| Datentypen | Bei Listen geht man oft davon aus, dass alle Elemente einen gemeinsamen Typ haben (ist aber keine Pflicht). | Die einzelnen Elemente in Tuples haben oft verschiedene Typen. |\n", + "| Performance | Wenig höherer Memory-Bedarf | Brauchen weniger Memory und weisen bei sehr grossen Datensätzen eine bisschen bessere Performance auf. |" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/03_collections/080Dictionaries.ipynb b/docs/03_collections/080Dictionaries.ipynb new file mode 100644 index 0000000..5375bcd --- /dev/null +++ b/docs/03_collections/080Dictionaries.ipynb @@ -0,0 +1,361 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Dictionaries\n", + "Im Folgenden Beispiel erstellen wir eine Zuweisung von Namen zu Email-Adressen mithilfe eines Dictionarys.\n", + "Dieses Dictionary soll speichern, welche Person welche Email-Adresse besitzt: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "email_database = {\n", + " \"John\": \"John.Guildmore@gmail.com\",\n", + " \"Jack\": \"jack_reacher@hotmail.com\"\n", + "}" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchten wir herausfinden, welche Person welche Email-Adresse besitzt, dann können wir das so tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "email = email_database[\"John\"]\n", + "email" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In den eckigen Klammern haben wir einen \"Key\" angegeben.\n", + "\n", + "Durch die Angabe des Keys in eckigen Klammern beim Dictionary erhalten wir den dazugehörigen Wert (\"Value\") zurück.\n", + "\n", + "Im Gegensatz zu Listen, Sets und Tuples erfolgt die Abfrage mit Keys anstelle von Indexen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Inhalte von Dictionarys müssen nicht von Beginn an bekannt sein, sondern können nachträglich ergänzt werden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "email_database = {} # leeres Dictionary\n", + "\n", + "# Werte hinzufügen:\n", + "email_database[\"John\"] = \"John.Guildmore@gmail.com\"\n", + "email_database[\"Jack\"] = \"jack_reacher@hotmail.com\"\n", + "\n", + "print(email_database[\"John\"]) # John.Guildmore@gmail.com\n", + "email_database" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operationen auf Dictionaries" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Das sind die wichtigsten Operationen auf Dictionaries:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_name = {} # Leeres Dictionary erzeugen.\n", + "\n", + "dict_name[\"key\"] = \"value\" # Einfügen bzw. ändern eines Elements.\n", + "variable = dict_name[\"key\"] # Eintrag lesen. Wirft einen Fehler, wenn der Eintrag nicht vorhanden ist.\n", + "dict_name.pop(\"key\") # Eintrag löschen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Auf der letzten Zeile haben wir den Eintrag mit dem Key \"key\" und den dazugehörigen Wert gelöscht.\n", + "\n", + "Möchten wir trotzdem den Wert für diesen Key abfragen, ohne dass ein Fehler geworfen wir, dann können wir das mit `.get(key)` tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "value = dict_name.get(\"key\")\n", + "print(value)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `dictionary.get(key)` macht das Gleiche wie `dictionary[key]` mit dem Unterschied, dass es `None` zurück gibt, falls der entsprechende Eintrag nicht vorhanden wäre.\n", + "* `dictionary[key]` hingegen würde einen Fehler werfen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Folgende Operationen können auch hilfreich sein:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_name = {\"key\": \"value\", \"key2\": \"value2\"}\n", + "# Selten werden auch diese Funktionen gebraucht:\n", + "del dict_name[\"key\"] # Eintrag löschen. Gängiger ist `dict_name.pop(\"key\")`.\n", + "dict_name.popitem() # Löscht den letzten hinzugefügten Eintrag.\n", + "dict_name.clear() # Löschen aller Elemente." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Durch ein Dictionary iterieren\n", + "\n", + "Du wirst in die Situation kommen, in welcher du durch ein Dictionary durchiterieren musst." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du z.B. alle Keys und Values durchgehen, dann kannst du das wie folgt tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_name = {\"key\": \"value\", \"key2\": \"value2\"}\n", + "\n", + "for key, value in dict_name.items():\n", + " print(f\"{key} has the value '{value}'.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du das Gleiche tun, aber nur mit den Keys, dann verwende `.keys()`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_name.keys()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "und das Gleiche für die Values:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_name.values()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## JSON in Python\n", + "Du wirst in Python ziemlich sicher einmal Daten in einem JSON-Format zu verarbeiten haben.\n", + "\n", + "Wenn du z.B. einen Server anfragst für bestimmte Daten, dann könntest du ein JSON-String wie diesen zurückbekommen:\n", + "\n", + "```json\n", + "{\n", + " \"name\": \"Colonel Toad\",\n", + " \"firstShared\": \"2011-08-01\",\n", + " \"origin\": \"Pinterest\"\n", + "}\n", + "```\n", + "\n", + "Ein JSON-String ist wie XML ein Format, um Daten in Text-Form zu strukturieren. Da JSON-Daten sehr ähnlich wie Dictionaries aufgebaut sind, können solche JSON-Daten in Python ganz einfach in ein Dictionary konvertiert werden.\n", + "\n", + "Du kannst wie folgt dieses JSON in ein Dictionary laden und entsprechende Operationen darauf ausführen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "json_string = '{\"name\": \"Colonel Toad\", \"firstShared\": \"2011-08-01\", \"origin\": \"Pinterest\"}'\n", + "\n", + "my_dict: dict[str, str] = json.loads(json_string)\n", + "my_dict" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die umgekehrte Richtung geht auch:\n", + "\n", + "Möchtest du ein Dictionary in ein JSON umwandeln, dann kannst du das mit `dumps(...)` tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "my_dict = {\"a\": 3, \"b\": \"Hello\"}\n", + "\n", + "json_string = json.dumps(my_dict)\n", + "json_string" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Typangabe\n", + "\n", + "Möchtest du bei einer Variable klar machen, dass es sich bei ihr um ein Dictionary handelt, dann kannst du das mit der Typangabe `: dict` tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_dict: dict = dict()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wenn der Type bei allen Keys und der Typ bei allen Values einheitlich sind, dann kannst du das auch angeben.\n", + "\n", + "Dies kann z.B. beim Laden eines JSON-Objekts hilfreich sein:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_dict: dict[str, int] = json.loads('{\"a\": 1, \"b\": 2}')\n", + "my_dict" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Bei der vorherigen Typangabe `dict[str, int]` haben wir angegeben, dass alle Keys Strings sind, und alle Values `int`-Werte sind." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Weitere Operationen auf Dictionaries findest du hier: https://www.w3schools.com/python/python_ref_dictionary.asp" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/040Conditions.py b/docs/040Conditions.py deleted file mode 100644 index e044531..0000000 --- a/docs/040Conditions.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -In Python gibt es natürlich auch bool'sche ausdrücke. Die meisten Operanden wie ==, <=, <, >, >=, != sollten bekannt sein und funktionieren gleich wie in Java. -Zusätzlich gibt es folgende Operatoren: - -and Funktioniert analog dem && Operator aus Java -or Funktioniert analog dem || Operator aus Java -in Gibt True zurück falls ein Element in einem Objekt (z.B. einer Liste) vorhanden ist, sonst False -is Im Gegensatz zum „==“ vergleicht der is-Operator nicht die Werte der Variablen, sondern die Instanzen selbst. Siehe Beispiel unten. -not Negiert einen bool'schen Ausdruck - -Im Folenden Beispiele werden alle print statements ausgeführt, weil alle if-Bedinungen zu True evaluieren: -""" - -###################################################################### - -a = True -b = False - -x = [1, 2, 3] -y = [1, 2, 3] - -print(x == y) # Output: True -print(x is y) # Output: False - -if a and b == False: - print("a is True and b is False.") - -if a or b: - print("Either a, b or both of them are True.") - -if 1 in x: - print("1 is in x.") - -if not b: - print("b is false.") - - -###################################################################### - diff --git a/docs/04_resource_management/041_exception_handling.ipynb b/docs/04_resource_management/041_exception_handling.ipynb new file mode 100644 index 0000000..fe83f69 --- /dev/null +++ b/docs/04_resource_management/041_exception_handling.ipynb @@ -0,0 +1,363 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exception Handling\n", + "Während des Ausführen von Python-Code können sehr viele Fehler passieren.\n", + "\n", + "Wenn Fehler erkannt werden, dann werden Fehler (in der Programmierung meistens Ausnahmen) geworfen (auf Englisch \"raise exceptions\").\n", + "\n", + "Fehler treten z.B. auf, wenn ein unerwarteter Input geliefert wird, oder wenn versucht wird, eine Zahl durch 0 zu teilen.\n", + "\n", + "Wenn wir wissen, dass ein bestimmter Code bestimmte Fehler werfen kann und wir diese Fehler abfangen wollen, dann können wir das mit den Keywords `try` und `except` tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " number = int(input(\"Please give a number: \"))\n", + " print(f\"100 / {number} = {100/number}\")\n", + "except Exception as exception:\n", + " print(type(exception))\n", + " print(exception)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Versuche beim obigen Code Fehler zu provozieren.\n", + "\n", + "Grundsätzlich gibt es bei diesem Code zwei häufige Fehlerquellen:\n", + "1. Der User gibt nicht eine Zahl sondern sonst etwas ein -> die Eingabe kann nicht in eine Zahl umgewandelt werden.\n", + " \n", + " Dies führt zu folgender Ausgabe:\n", + " ```\n", + " \n", + " invalid literal for int() with base 10: 'ungültige Zahl'\n", + " ```\n", + "2. Der User gibt die Zahl 0 ein -> dies führt zu einer 0-Division.\n", + " \n", + " Dies führt zu dieser Ausgabe:\n", + " ```\n", + " \n", + " division by zero\n", + " ```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Oft wirst du in die Situation kommen, dass du verschiedene Fehler verschieden oder nur bestimmte Fehler behandeln möchtest.\n", + "Hierfür bietet es sich an, verschiedene `except`-Blöcke zu definieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " number = int(input(\"Please give a number: \"))\n", + " print(f\"100 / {number} = {100/number}\")\n", + "except ZeroDivisionError:\n", + " print(\"Use another input than 0. Can't divide by 0.\")\n", + "except ValueError:\n", + " print(\"Please enter a valid number!\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nach dem `except`-Keyword haben wir den Typ der Exception (des Fehlers) angegeben. Der Inhalt des entsprechenden `except`-Blocks wird nur ausgeführt, wenn der Fehler vom spezifizierten Typ ist (bzw. davon erbt).\n", + "\n", + "Im allerersten `except` haben wir die Exception-Klasse `Exception` angegeben. Da die anderen Exception-Klassen wie `ZeroDivisionError` und `ValueError` eine Subklasse von `Exception` sind, werden diese Fehler auch von `except Exception` abgefangen.\n", + "\n", + "Die beiden genaueren Exceptions dienen für folgenden Zweck:\n", + "* `ZeroDivisionError`: Wird geworfen, wenn eine Zahl durch 0 geteilt wird.\n", + "* `ValueError`: Ein Parameter (hier derjenige für `input`) hat einen nicht unterstützen Wert erhalten." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kürzestes Beispiel\n", + "Nehmen wir noch einmal das erste Beispiel und vereinfachen es so, dass wir nur die Information erhalten, dass ein Fehler aufgetreten ist - nicht aber, welcher.\n", + "\n", + "Das kann auf folgendes reduziert werden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " number = int(input(\"Please give a number: \"))\n", + " print(f\"100 / {number} = {100/number}\")\n", + "except:\n", + " print(\"An error has occurred. This is all I know :/\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dieses Beispiel sollte zeigen, dass **nicht** zwingend eine `Exception`-Klasse angegeben werden muss.\n", + "\n", + "Dies ist eine schnelle Möglichkeit, ein Exception-Handling zu schreiben, das alle `Exception`s abfängt. In produktivem Code wirst du meisten nicht so ein einfaches Exception-Handling sehen, weil auf diese Weise die Information verloren geht, was der Fehler war." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Vollständiges Beispiel\n", + "Der Vollständigkeit halber präsentieren wir hier ein komplettes `try-except-else-finally`-Exception Handling:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " number = int(input(\"Please give a number: \"))\n", + " result = 100/number\n", + "except ZeroDivisionError:\n", + " print(\"Use another input than 0. Can't divide by 0.\")\n", + "except ValueError:\n", + " print(\"Please enter a valid number!\")\n", + "except Exception as ex:\n", + " print(\"An exception occurred that I didn't think of :/\")\n", + " print(type(ex))\n", + " print(ex)\n", + "else:\n", + " print(\"The operation succeeded. The result:\", result)\n", + "finally:\n", + " print(\"Now, let's go into vacations :)\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Der obige Code beinhaltet\n", + "* das ursprüngliche Error-Handling, das auf `ZeroDivisionError`- und `ValueError`-Fehler behandelt.\n", + "* einen Exception-Block, der alle anderen `Exception`s abfängt, die noch nicht abgefangen wurden (`except Exception as ex`-Block)\n", + "* einen `else`-Block, der ausgeführt wird, wenn es im `try` keinen Fehler gegeben hat.\n", + "* und einen `finally`-Block, der **immer** im Anschluss noch ausgeführt wird (ganz egal, ob eine Exception aufgetreten ist oder nicht)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exceptions werfen\n", + "Wenn du z.B. ungültigen Input erhältst, dann kann es sinnvoll sein, dass deine Methode einen Fehler wirft.\n", + "\n", + "Im folgenden Beispiel erwartet deine Funktion eine Zahl als Input, Strings hingegen machen keinen Sinn im Parameter. Deswegen kann es Sinn machen, den Input zu überprüfen und bei einem falschen Typ eine `Exception` zu werfen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def square(x: int | float) -> int | float:\n", + " \"\"\"Returns the square of x.\"\"\"\n", + "\n", + " if not isinstance(x, (int, float)):\n", + " raise ValueError(\n", + " \"The input of the square function must be a non-complex number\"\n", + " )\n", + "\n", + " return x * x\n", + "\n", + "\n", + "square(2)\n", + "square(2.0)\n", + "square(\"2\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel war folgende Zeile zentral:\n", + "\n", + "```python\n", + "raise ValueError(\"message\")\n", + "```\n", + "\n", + "Mit dem `raise`-Keyword wirf man die Exception rechts von diesem Keyword.\n", + "\n", + "In diesem Beispiel haben wir eine `Exception` vom Typ `ValueError` geworfen. Dieser Typ deutet an, dass eine Funktion it einem ungültigen Wert/Argument aufgerufen wurde.\n", + "\n", + "Ganz allgemein können alle Objekte als Fehler geworfen werden, die eine Instanz von einer Exception-Klasse sind:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "raise Exception(\"A random exception\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Angeben, wenn Fehler geworfen wird\n", + "Es ist eine Good-Practice, im DocString einer Funktion/Methode anzugeben, wenn sie eine Exception wirft. Somit weiss dann auch der/die Entwickler:in, welche `Exception` behandelt werden muss.\n", + "\n", + "Der DocString aus dem `square(x)`-Beispiel könnte wie folgt aussehen (Beachte den DocString-Block `Raises`):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def square(x: int | float) -> int | float:\n", + " \"\"\" \n", + " Returns the square of x.\n", + " \n", + " Parameters:\n", + " - x (int | float): The number you want the square of.\n", + "\n", + " Returns:\n", + " - (int | float): The square of x.\n", + "\n", + " Raises:\n", + " - ValueError: If x is not a number.\n", + " \n", + " \"\"\"\n", + "\n", + " if not isinstance(x, (int, float)):\n", + " raise ValueError(\"The input of the square function must be a non-complex number\")\n", + " \n", + " return x * x" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Neue Exception-Typen definieren\n", + "Es kann schnell vorkommen, dass Python keine Exception-Klasse für einen sehr spezifischen Fehler bereitstellt.\n", + "\n", + "In diesem Fall wird oft eine neue Art von Exception geschrieben. Hierfür wird eine neue Klasse erstellt, die von der Klasse `Exception` erbt.\n", + "\n", + "(Vererbung wird erst später behandelt. Deswegen lasse dich nicht von `__init__`, `self`, usw. verwirren. Zentral ist hier nur, dass du weisst, dass du eigene Exceptions definieren kannst.)\n", + "\n", + "Im folgenden Beispiel erstellen wir die neue Exception `NegativeNumberException`, die normalerweise geworfen wird, wenn die Quadratwurzel aus einer negativen Zahl genommen wird:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class NegativeNumberException(Exception):\n", + " def __init__(self, number):\n", + " super().__init__(f\"Cannot compute square root of a negative number: {number}\")\n", + "\n", + "\n", + "def square_root(number) -> float:\n", + " if number < 0:\n", + " raise NegativeNumberException(number)\n", + "\n", + " # ^ 0.5 is equals to take the square root:\n", + " return number ** 0.5" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Diese eigene Exception hat den Vorteil, dass diese Exception besser abgefangen werden kann:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def print_square_root(number):\n", + " result: float | complex = None\n", + "\n", + " try:\n", + " result = square_root(number)\n", + " except NegativeNumberException:\n", + " # if number < 0: We can't direct calculate the square root.\n", + " # But thanks to a trick, we know that square_root(a * b) = square_root(a) * square_root(b).\n", + " # And for something smaller than 0:\n", + " # square_root(-n) = square_root(-1) * square_root(n) = i * square_root(n)\n", + " # where i is the complex number (in Python represented as `j`).\n", + " result = 1j * square_root(-1 * number)\n", + " \n", + " print(f\"The square root of {number} is {result}\")\n", + "\n", + "\n", + "print_square_root(1)\n", + "print_square_root(-1)\n", + "print_square_root(4)\n", + "print_square_root(-4)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.4" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/04_resource_management/043_file_io.ipynb b/docs/04_resource_management/043_file_io.ipynb new file mode 100644 index 0000000..30ccc57 --- /dev/null +++ b/docs/04_resource_management/043_file_io.ipynb @@ -0,0 +1,284 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# I/O: Dateien lesen und schreiben\n", + "\n", + "Python macht das lesen und beschreiben von Dateien sehr einfach." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Datei lesen\n", + "Möchtest du eine Datei lesen, dann kannst du das wie folgt tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "file = open('./random_text.txt', 'r')\n", + "\n", + "# Get the text out of the file:\n", + "content = file.read()\n", + "print(content)\n", + "\n", + "# Close the file afterwards so that this file isn't blocked:\n", + "file.close()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit `open(...)` können wir eine Datei laden. Das erste Argument ist der Pfad zur Datei. Das zweite Argument ist optional, wir haben hier `'r'` angegeben, damit klar ist, dass wir die Datei nur lesen (read) möchten. Wird das zweite Argument `r` nicht angegeben, so kann die Datei auch nur gelesen werden.\n", + "\n", + "Die Methode `read()` hat uns den Text-Inhalt der Datei zurückgegeben und mit der `close()`-Methode schliessen wir die Datei wieder, damit der Zugriff auf sie wieder freigegeben wird.\n", + "\n", + "Nun können wir dies noch ein bisschen umschreiben:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('./random_text.txt', 'r') as file:\n", + " \n", + " for line in file:\n", + " line = line.strip()\n", + " \n", + " print(line)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Im letzten Beispiel haben wir nun vom `with`-Statement Gebrauch gemacht. Dieses `with` sorgt dafür, dass automatisch in jedem Fall `file.close()` aufgerufen wird. Dateien sollten immer wieder geschlossen werden, damit der Zugriff auf diese nicht für andere blockiert wird.\n", + "\n", + "Zusätzlich kann man mit `for line in file` durch alle Zeilen durch iterieren. Dies verhindert gleichzeitig auch eine hohe Speicherauslastung, weil so nur Zeile für Zeile gelesen und in den Arbeitsspeicher kopiert wird (statt die ganze Datei auf einmal).\n", + "\n", + "`string.strip()` wird hier noch verwendet, weil meistens (immer ausser bei der letzten Zeile) am Ende der Zeile noch das Zeichen `\\n` bzw. `\\n\\r` folgt. Mit der `strip()`-Methode wird dies entfernt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Datei schreiben\n", + "Möchtest du Text in eine Datei schreiben, dann kannst du das wie folgt tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('./random_output.txt', 'w') as file:\n", + " \n", + " file.write(\"Hello, it's me!\\n\")\n", + " file.write('Hello from the other side.\\n')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Beachte das `'w'` für write (also schreiben) in der `open(...)`-Methode. Dieses musst du angeben, damit du in die Datei schreiben kannst.\n", + "\n", + "Mit `write(...)` fügst du Text der Datei hinzu.\n", + "\n", + "Bitte beachte hier, dass das `'w'` bewirkt, dass eine bestehende Datei überschrieben wird. Möchtest du nur Text hinzufügen, dann verwende `'a'` wie 'append' (hinzufügen)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Datei löschen\n", + "\n", + "Wie folgt kannst du eine Datei löschen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.remove('./random_output.txt')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Du musst `os` importieren, weil die Funktion `remove(...)` in diesem Modul namens `os` ausgelagert ist." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Analog zum Löschen einer Datei kannst du auch einen Ordner löschen. Dies kannst du mit der `rmdir`-Funktion erledigen:\n", + "\n", + "```python\n", + "import os\n", + "\n", + "os.rmdir('./path/to/folder')\n", + "```\n", + "\n", + "Hierfür muss der Ordner aber leer sein!" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ist der Ordner nicht leer, dann kannst du ihn inkl. Inhalt mit Hilfe von `shutil` aus der Python Standard Library löschen (keine weitere Installation notwendig):\n", + "\n", + "```python\n", + "import shutil\n", + "\n", + "shutil.rmtree('./path/to/folder')\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exception Handling\n", + "Im Zusammenhang mit Dateien passieren immer wieder Fehler.\n", + "Daher macht es Sinn, folgende Fehler auch gleich zu behandeln:\n", + "* `FileNotFoundError`: Wenn die Datei nicht gefunden werden konnte.\n", + "* `IOError`: Andere Fehler, die im Zusammenhang mit dem Dateisystem auftreten können.\n", + "\n", + "Dies könnte so aussehen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " with open('./random_text.txt', 'r') as file:\n", + " for line in file:\n", + " line = line.strip()\n", + " print(line)\n", + "except FileNotFoundError:\n", + " print(\"Can't find the file.\")\n", + "except IOError:\n", + " print(\"An IO error occurred.\")\n", + "except Exception as e:\n", + " print(\"An unknown error occurred:\", e)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Prüfen, ob Datei existiert\n", + "\n", + "Vorher haben wir den Fall, in welchem eine Datei nicht existiert, mit einem Exception-Handling (`try`-`except`) abgefangen.\n", + "\n", + "In vielen Fällen ist es völlig normal, dass eine bestimmte Datei (noch) nicht existiert (z.B. beim ersten Mal ausführen deines Skriptes).\n", + "\n", + "In diesem Fall macht es dann mehr Sinn zu ermitteln, ob die Datei existiert - anstatt einen Fehler zu provozieren. Das kannst du wie folgt tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os.path\n", + "\n", + "if os.path.isfile('./random_text.txt'):\n", + " print(\"File exists.\")\n", + "else:\n", + " print(\"File does not exist :/\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Vergiss' hier nicht, `os.path` zu importieren, damit du die `isfile`-Funktion verwenden kannst." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Du kannst nicht nur Dateien auf deren Existenz überprüfen sondern auch Ordner:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os.path\n", + "\n", + "if os.path.isdir('/Users/'):\n", + " print('You are on a Mac.')\n", + "elif os.path.isdir('C:/Users/'):\n", + " print('You are working on a Windows computer')\n", + "else:\n", + " print(\"Can't detect your operating system.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.4" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/04_resource_management/random_text.txt b/docs/04_resource_management/random_text.txt new file mode 100644 index 0000000..ab65d13 --- /dev/null +++ b/docs/04_resource_management/random_text.txt @@ -0,0 +1,3 @@ +Why did Python software engineers go to Atlantis? + +To see if they could use Pydantic models to validate the properties of the mythical sea creatures, and to check if they could apply Pydantic's strict type checking to the underwater ecosystem! \ No newline at end of file diff --git a/docs/050Loops.py b/docs/050Loops.py deleted file mode 100644 index 42f6597..0000000 --- a/docs/050Loops.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -For-Loop - -Es gibt mehrere Möglichkeiten, einen For-Loop in Python zu implementieren. -Eine Möglichkeit ist die Benutzung der range() methode, welche wiederum selbst verschieden eingesetzt werden kann. -Eine andere Möglichkeit ist einfach über alle Elemente einer Liste (oder auch eines Sets, Dictionarys, ...) zu iterieren. -Beachte den Doppelpunkt und die Einrückung des Bodys. -Das Nachfolgende Beispiel sollte Klarheit verschaffen. -""" - -###################################################################### - -for i in range(10): # i = 0, 1, 2, ... , 9 - print(i) - -for i in range(3, 8): # i = 3, 4, 5, 6, 7 - print(i) - -for i in range(2, 9, 3): # i = 2, 5, 8 - print(i) - -numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -for number in numbers: # prints out every number in the numbers list - print(number) - -###################################################################### - -""" -While-Loop - -Der While Loop funktioniert genau wie in Java auch. Hier ein Beispiel: - -""" - -###################################################################### - -i = 0 -while i < 5: - print(i) - i += 1 - -###################################################################### - -""" -Die Statements "break" and "continue" sollten bekannt sein und funktionieren ebenfalls wie in Java. -""" - - -# https://www.learnpython.org/en/Loops -# Can we use "else" clause for loops? diff --git a/docs/05_advanced_techniques/05_1_regex.ipynb b/docs/05_advanced_techniques/05_1_regex.ipynb new file mode 100644 index 0000000..35313db --- /dev/null +++ b/docs/05_advanced_techniques/05_1_regex.ipynb @@ -0,0 +1,243 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# RegEx\n", + "\n", + "Als Entwickler:in wirst du eines Tages viel Informationen aus einem strukturierten Text herauslesen müssen oder einen Input gem. Schema validieren (sprich: überprüfen) müssen.\n", + "\n", + "Möchtest du z.B. vor dem Abschicken eines Web-Formulars überprüfen, ob eine Eingabe eine gültige Email-Adresse ist, dann kannst du hier auch RegEx brauchen. Bei einer Email-Adresse weisst du, dass diese aus einem Teil vor einem \"@\", und einem Teil danach, der wiederum mindestens einen Punkt besitzen muss, besteht. Diese Regel kannst du in einer RegEx (Regular Expression, deutsch: regulärer Ausdruck) beschreiben:\n", + "\n", + "```regex\n", + "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$\n", + "```\n", + "\n", + "In diesem Beispiel hast du\n", + "* vor dem \"@\" Buchstaben, Zahlen sowie \".\", \"_\", \"+\" und \"-\" erlaubt\n", + "* Nach dem \"@\" Müssen Buchstaben, Zahlen, Punkte oder Bindestriche folgen\n", + "* und die Email muss mit einem Punkt und dann mindestens 2 Buchstaben enden.\n", + "\n", + "> _Eine RegEx ist also eine Folge von Zeichen, die ein Suchmuster bilden. RegEx wird dafür verwendet, um Text anhand von einem Muster (einer Regel) abzugleichen._\n", + "\n", + "\n", + "Mit RegEx kannst du ja überprüfen, ob ein Text einem bestimmten Muster (\"pattern\") entspricht. Versuche unter folgenden Links die vorher gezeigte RegEx einzugeben und zu prüfen, wann eine Email-Adresse erkannt wird und wann nicht:\n", + "* https://regex101.com/: Sehr bekannt und praktisch für RegEx.\n", + "* https://pythex.org/: Optimiert für die Verwendung unter Python.\n", + "\n", + "Hinweis: Die obengenannte RegEx für Email-Adressen ist bei weitem nicht vollständig. Eine richtig funktionierende und vollständige RegEx für Email-Adresse ist viel komplexer." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Einfache RegEx erstellen\n", + "Beginnen wir mit einem einfacheren Beispiel. Sagen wir, wir haben einen Text wie:\n", + "\n", + "```text\n", + "destroy_world: True\n", + "```\n", + "\n", + "Nun wollen wir herausfinden, ob nach `destroy_world` True steht, damit unser Programm entsprechend handeln kann.\n", + "\n", + "Beginnen wir mit einem sehr einfachen Vorgehen: Wir prüfen nur, ob `True` vorkommt:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re # re wird gebraucht für Regex.\n", + "\n", + "text = \"destroy_world: True\"\n", + "pattern = r\"True\"\n", + "\n", + "result = re.search(pattern, text)\n", + "if result is not None:\n", + " print(\"Sure. Your wish is my command :D\")\n", + " print(\"The variable was set to\", result.group())\n", + "else:\n", + " print(\"So then, I don't do anything.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Im obigen Beispiel haben wir nur geprüft, ob der Wert `True` vorhanden ist. Wenn wir nun einen Schritt weitergehen und auch `False` finden möchten, dann könnten wir das wie folgt erweitern:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "text = \"destroy_world: True\"\n", + "pattern = r\"True|False\"\n", + "\n", + "result = re.search(pattern, text)\n", + "if result is not None:\n", + " print(\"The variable was set to\", result.group())" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hier haben wir nun gesehen, dass wir eine Oder-Beziehung reinbringen konnten: `pattern = r\"True|False\"`.\n", + "\n", + "Ein `|` fungiert in einer Regex wie ein \"Oder\".\n", + "\n", + "Gehen wir nun noch einen Schritt weiter und erwarten nicht einen Boolean sondern einen Namen, dann könnten wir so vorgehen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re # re wird gebraucht für Regex.\n", + "\n", + "text = \"destroy_world: Earth\"\n", + "pattern = r\": [a-zA-Z]+\"\n", + "\n", + "result = re.search(pattern, text)\n", + "if result is not None:\n", + " print(f\"The variable was set to '{result.group()[2:]}'\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel hatten wir die Regex `: [a-zA-Z]+`.\n", + "* Diese beschreibt, dass das Gesuchte mit \": \" beginnt.\n", + "* Anschliessend kommt ein Zeichen, das ein (klein) Buchstaben zwischen a und z oder ein Grossbuchstabe zwischen A und Z.\n", + " * Solche Ranges werden in eckige Klammern gepackt.\n", + "* Mit dem `+` wurde spezifiziert, dass das in den eckigen Klammern **mindestens 1 mal** vorkommt.\n", + "* Mit dem `[2:]` haben wir ein bisschen gebastelt: Wir wollen das \": \" im Resultat loswerden und schneiden das Resultat so zu, dass es erst beim 3. Zeichen beginnt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Gruppen\n", + "Sehr oft suchen wir nach bestimmten Wörtern/Zahlen in einem bestimmten Umfeld/Kontext.\n", + "\n", + "Angenommen wir haben einen Text wie\n", + "```yaml\n", + "app_name: PacMan\n", + "version: 1.0.1\n", + "app_image: C:\\Users\\PacMan\\Documents\\pacman.png\n", + "```\n", + "\n", + "Mit Hilfe von \"Named Groups\" können wir den Werten in der Regex einen Namen geben. Das hat den Vorteil, dass wir im Prinzip den erwarteten Text schreiben können und die spezifischen Werte via ihren Namen ausschneiden können:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "text = \"\"\"app_name: PacMan\n", + "version: 1.0.1\n", + "app_image: C:\\\\Users\\\\PacMan\\\\Documents\\\\pacman.png\"\"\"\n", + "\n", + "# RegEx pattern that matches for all variable names.\n", + "pattern = r\"app_name:\\s*(?P[a-zA-Z0-9]+)\\nversion:\\s*(?P\\d+\\.\\d+\\.\\d+)\\napp_image:\\s*(?P.+)\"\n", + "\n", + "match = re.search(pattern, text)\n", + "\n", + "# Extract the values\n", + "app_name = match.group(\"app_name\")\n", + "version = match.group(\"version\")\n", + "app_image = match.group(\"app_image\")\n", + "\n", + "print(app_name, version, app_image)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit einem Konstrukt wie `(?P\\[a-zA-Z0-9]+)` haben wir eine solche Named Group erstellt. Der Teil `?P` definiert den Namen der Named Group (hier \"app_name\"), `[a-zA-Z0-9]+` ist die Regex für den Wert der Named Group \"app_name\", und die Klammern sind zur besseren Abgrenzung da.\n", + "\n", + "Wenn du dich noch fragst, was das `?P` soll: Das `P` steht für Python und das ist eine Regex, die für Python's Regex-System funktioniert. In anderen Programmiersprache wirst du eine andere Syntax dafür verwenden." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Spezielle Sequenzen\n", + "Du hast bereits erfahren, dass du mit einer Regex wie `[a-zA-Z0-9]` alle englischen Buchstaben (also keine ÄÖÜ) inkl. Zahlen und Unterstrich einschliessen kannst.\n", + "\n", + "Für genau solche oft vorkommenden Regeln gibt es Abkürzungen.\n", + "* Statt `[a-zA-Z0-9]` kannst du auch einfach `\\w` verwenden.\n", + "* Statt \" \" kannst du auch `\\s` verwenden, der jeden Whitespace(\" \", \\t, \\n, \\r, \\f, \\v) mit einschliesst.\n", + "* Für Zahlen kannst du `\\d` (wie \"digit\") verwenden.\n", + "\n", + "## Mengenangaben\n", + "Oft musst du angeben, wie viel mal ein bestimmtes Zeichen vorkommt. Das hast du z.B. bei dieser Regel getan: `[a-zA-Z0-9]+`\n", + "Mit dem `+` hast du angegeben, dass **mindestens ein** solches Zeichen vorkommen muss. Andere Mengenangaben sind:\n", + "* `*`: 0, 1 oder mehrere Vorkommnisse.\n", + "* `?`: 0 oder 1 mal.\n", + "* `{n}`: Genau n-mal.\n", + "* `{m, n}`: Mindestens m-mal, maximal n-mal.\n", + "* `{m,}`: Mindestens m-mal.\n", + "* `{,n}`: Maximal n-mal.\n", + "\n", + "## Spezielle Charakter (Zeichen)\n", + "Sehr oft siehst du eine Regex in diesem Format: `^irgend eine RegEx$`. Oft wird erwartet, dass der ganze String (also Text) genau einer RegEx entspricht und nicht nur ein Teil von ihm. Das `^` steht für den Anfang, das `$` für das Ende des Textes (bzw. der Zeile, je nach Kontext).\n", + "\n", + "Sehr oft kommt es vor, dass du ein bestimmtes Zeichen wie z.B. den Punkt `.` als Zeichen erwartest. Generell können Sonderzeichen mit einem `\\` \"escaped\" werden.\n", + "\n", + "Apropos Punkt `.`: Dieser steht für irgendein Zeichen.\n", + "\n", + "Weitere Informationen findest du hier, wenn du das Cheatsheet aufklappst: https://pythex.org/." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.10.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/05_advanced_techniques/05_2_turtles.ipynb b/docs/05_advanced_techniques/05_2_turtles.ipynb new file mode 100644 index 0000000..1519d34 --- /dev/null +++ b/docs/05_advanced_techniques/05_2_turtles.ipynb @@ -0,0 +1,182 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Turtles" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pythons `turtle`-Modul bietet Tools an, mit denen man interaktiv visuelle Formen auf einem Fenster zeichnen kann.\n", + "\n", + "Mit sogenannten Turtles kannst du auf eine einfache Art und Weise etwas visuelles aufsetzen wie z.B. ein kleines Spiel. Hierbei wird eine sogenannte Turtle - also Schildkröte - via Code gesteuert und hinterlässt eine Farbspur, die auf dem Bildschirm bleibt.\n", + "\n", + "Hinweis: Das `turtle`-Modul funktioniert je nach Betriebssystem und Python-Version nicht in diesem Jupyter-Notebook. Schreibe daher deinen `turtle`-Code ausserhalb eines Jupyter-Notebooks, z.B.\n", + "* in gewöhnlichen Python-Dateien (`.py`) in VS Code oder PyCharm\n", + "* oder mit dem mit Python dazuinstallierten Programm IDLE (im [aller ersten Kapitel](../01_basics/000Intro.ipynb) im Abschnitt \"IDLE\" vorgestellt)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basics\n", + "\n", + "Um die Turtles brauchen zu können, musst du ein Objekt von der Klasse `Turtle` instanziieren (was automatisch ein Canvas/Fenster öffnet):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mThe Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click here for more info. View Jupyter log for further details." + ] + } + ], + "source": [ + "from turtle import Turtle\n", + "\n", + "turtle1 = Turtle()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Anschliessend kannst du der Turtle sagen, wo es lang geht:\n", + "* mit `forward(pixel)` um `pixel` vorwärts bewegen.\n", + "* mit `backward(pixel)` um `pixel` rückwärts bewegen.\n", + "* mit `left(degrees)` um `degrees` nach links rotieren.\n", + "* mit `right(degrees)` um `degrees` nach rechts rotieren.\n", + "\n", + "Siehe dieses Beispiel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "turtle1.forward(100)\n", + "turtle1.left(90)\n", + "turtle1.forward(100)\n", + "turtle1.left(90)\n", + "turtle1.forward(100)\n", + "turtle1.left(90)\n", + "turtle1.forward(100)\n", + "turtle1.right(45)\n", + "turtle1.backward(141)\n", + "turtle1.right(135)\n", + "turtle1.backward(100)\n", + "turtle1.left(45)\n", + "turtle1.forward(141)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Um neu zu starten, kannst du ungefähr folgendes eingeben:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "turtle1.setpos(0, 0) # reset the position.\n", + "turtle1.setheading(0) # make the turtle look to the right\n", + "turtle1.clear() # clear the canvas." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ungefähr so kannst du auch die Farben und Stiftgrösse verändern:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "turtle1.pencolor(\"red\") # Set pen color to red\n", + "turtle1.pensize(5) # Set pen size to 5\n", + "turtle1.fillcolor(\"blue\") # Set fill color to blue\n", + "turtle1.begin_fill() # Start filling shape\n", + "turtle1.circle(50) # Draw a circle\n", + "turtle1.end_fill() # End filling shape" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Und du kannst auch ein kleineres Spiel realisieren.\n", + "\n", + "Du kannst z.B. auf Tastatur-Events reagieren (funktioniert aber nicht ohne Weiteres in Jupyter Notebooks):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from turtle import listen, onkey, Turtle\n", + "\n", + "\n", + "def turn_left(turtle1: Turtle):\n", + " print(\"left\")\n", + " turtle1.left(10)\n", + " turtle1.forward(100)\n", + "\n", + "onkey(lambda: turn_left(turtle1), \"Left\") # Call turn_left function when \"Left\" key is pressed\n", + "listen() # Start listening for events" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/05_advanced_techniques/05_3_external_programs.ipynb b/docs/05_advanced_techniques/05_3_external_programs.ipynb new file mode 100644 index 0000000..b6848db --- /dev/null +++ b/docs/05_advanced_techniques/05_3_external_programs.ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Mit externen Programmen kommunizieren" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python bietet die Möglichkeit an, andere Prozesse zu starten und mit ihnen zu kommunizieren." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wenn du den Kommandozeilenbefehl kennst, um ein externes Programm auszuführen, dann ist es auch nicht schwer, dieses mit Python auszuführen.\n", + "\n", + "Möchtest du z.B. den Namen des aktuellen Users herausfinden, dann könntest du folgenden Code (ohne \"!\") in deinem Terminal eingeben:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "shellscript" + } + }, + "outputs": [], + "source": [ + "!whoami" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dieser Befehl gibt dir den aktuellen Benutzernamen zurück. Diesen Befehl kannst du auch via Python ausführen lassen, wobei das Ergebnis des Befehls gleich ausgegeben wird:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "\n", + "\n", + "subprocess.call(['whoami'])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du die Ausgabe in einer Python-Variablen speichern können, dann verwende eine andere Funktion:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "user_name = subprocess.check_output([\"whoami\"])\n", + "\n", + "print(f\"Your user name is {user_name}.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In der Ausgabe stellst du fest, dass die Rückgabe ein Byte-Array statt einem String ist. Um `b'...'` loszuwerden, musst du die Rückgabe noch dekodieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "user_name = subprocess.check_output([\"whoami\"]).decode(\"utf-8\")\n", + "# Rufe noch `.strip()` auf, um die neue Zeile am Ende zu entfernen.\n", + "\n", + "print(f\"Your user name is {user_name}.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installierte Programme ausführen" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit Pythons `subprocess`-Modul können natürlich auch heruntergeladene installierte Programme aufgerufen werden.\n", + "\n", + "Z.B. kannst du auch Git-Befehle (hierfür muss das Programm \"Git\" installiert sein) ausführen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current_branch = subprocess.check_output([\"git\", \"branch\", \"--show-current\"]).decode(\"utf-8\").strip()\n", + "\n", + "print(f'You are now working on branch \"{current_branch}\". :)')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Möglichkeiten beschränken sich natürlich nicht auf Konsoleanwendungen. Du kannst auch Anwendungen mit graphischem User-Interface ausführen.\n", + "\n", + "Wenn du VS Code installiert hast und VS Code mit dem Befehl `code` starten kannst, dann kannst du via Python auch ein neues Fenster wie folgt aufmachen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Öffne VS Code mit dem Ordner \"05_advanced_techniques\":\n", + "\n", + "subprocess.call([\"code\", \"-n\", \"../05_advanced_techniques\"])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Windows wird dieser Befehl vielleicht scheitern, weil kein Programm auf den Namen \"code\" im System registriert wurde, nur ein Kommandozeilenbefehl.\n", + "\n", + "Möchtest du einfach den Kommandozeilenbefehl ausführen, dann kannst du das mit `shell=True` tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "subprocess.call([\"code\", \"-n\", \"../05_advanced_techniques\"], shell=True) # Macht evtl. dann bei Mac nichts mehr..." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit `shell=True` hast du mitgeteilt, dass du ein Kommandozeilenbefehl ausführen möchtest. Wenn nicht zwingend notwendig, sollte darauf verzichtet werden, weil so einfacher bösartiger User-Code eingeführt werden könnte, wenn der User irgendwie Zugriff auf die Parameter erhält.\n", + "\n", + "Eine andere Möglichkeit wäre es herauszufinden, welches Programm eigentlich gestartet werden soll.\n", + "\n", + "Das kann z.B. mit dem `where`-Befehl gemacht werden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Herausfinden, wo VS Code installiert ist:\n", + "command = \"where\" # TODO: \"which\" on MacOS.\n", + "vscode_path = subprocess.check_output([command, \"code\"]).decode(\"utf-8\").strip().replace(\"\\r\", \"\").split(\"\\n\")[-1]\n", + "\n", + "print(\"Path to VS Code:\", vscode_path)\n", + "\n", + "# Open VS Code:\n", + "subprocess.call([vscode_path, \"-n\", \"../05_advanced_techniques\"])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mit Programmen kommunizieren" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Manchmal musst du auch mit einem gestarteten Prozess später noch kommunizieren können, wenn z.B. eine Eingabe erwartet wird.\n", + "\n", + "Vorher hast du Funktionen verwendet, die im `subprocess`-Modul angeboten wurden. Diese Funktionen sind im Prinzip Vereinfachungen von der `Popen`-Klasse, bzw. verwenden sie sogar.\n", + "\n", + "Möchtest du später noch mit einem Prozess kommunizieren können, dann musst du die Klasse `Popen` direkt verwenden.\n", + "\n", + "Siehe dir zuerst unser Script an, das eine Eingabe erwartet: [05_3_user_interaction.py](./05_3_user_interaction.py).\n", + "\n", + "Mit dem folgenden Code können wir dieses Script starten und eine Eingabe einschleusen, sobald sie erwartet wird (Achtung: je nach Betriebssystem und Python-Installation lautet der Befehl nicht `python3` sondern `python`, `python3.11`, `py` oder so ähnlich):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from subprocess import Popen, PIPE\n", + "\n", + "# Möglicherweise ist `python3` nicht als Befehl vorhanden. Ersetze ihn wenn nötig mit deinem.\n", + "# Dies startet das Python-Script:\n", + "with Popen([\"python3\", \"./05_3_user_interaction.py\"], stdin=PIPE, stdout=PIPE, stderr=PIPE) as process:\n", + " \n", + " # Hier schleusen wir \"Luigi\" ein, damit das die Funktion `input()` erhält:\n", + " stdout, stderr = process.communicate(\"Luigi\".encode(\"utf-8\"))\n", + " \n", + " print(stdout, stderr)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dieser Code sieht ein bisschen kompliziert aus, ist es aber nicht ;)\n", + "\n", + "* Mit `Popen(...)` (Konstruktor) haben wir einen neuen Prozess geöffnet (`Popen`: \"Process Open\").\n", + "* `stdin`, `stdout` und `stderr` bezeichnen verschiedene Kanäle für die Kommunikation:\n", + " * `stdin` wird verwendet, um von unserer Seite Input in den Prozess einzuschleusen.\n", + " * `stdout` ist der Kanal, in welchem die Ausgabe des Prozesses erreichbar ist.\n", + " * `stderr` ist der Kanal, in welchem Fehler erreichbar sind.\n", + "* Wir haben diesen Parametern den Wert `subprocess.PIPE` gegeben, damit wir diese Kanäle verwenden können. Ansonsten würden diese an ein User-Terminal weitergeleitet bzw. der Output würde direkt ausgegeben werden, anstelle das wir Zugriff auf diese Kanäle hätten.\n", + "\n", + "* Mit `process.communicate(...)` führen wir den Prozess weiter, bis eine Eingabe erwartet wird.\n", + " * Sobald die Eingabe erwartet wird, übergeben wir dem Prozess den String \"Luigi\", damit dies als User-Eingabe (`input()`-Funktion) erkannt wird.\n", + "\n", + "* Am Schluss sollte der Output in der Variable `stdout` sein und die Variable `stderr` sollte ein leeres Byte-Array sein - ausser es gab einen Fehler.\n", + "\n", + "So, das war bereits die ganze Hexerei!\n", + "\n", + "Versuche nun, die [Lab-Aufgaben](../../labs/05_advanced_techniques/05_3_external_programs.ipynb) zu lösen!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/05_advanced_techniques/05_3_user_interaction.py b/docs/05_advanced_techniques/05_3_user_interaction.py new file mode 100644 index 0000000..34ef33a --- /dev/null +++ b/docs/05_advanced_techniques/05_3_user_interaction.py @@ -0,0 +1,5 @@ +print("Hello, welcome to the world's best program.") + +user_name = input("Please enter your Name: ") + +print(f"Hi {user_name}, nice to meet you!") \ No newline at end of file diff --git a/docs/060Functions.py b/docs/060Functions.py deleted file mode 100644 index 53359da..0000000 --- a/docs/060Functions.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Kommen wir zum Thema Funktionen. -Eine Funktion in Python ist das, was wir in Java eine Methode nennen. Das Gundprinzip sollte klar sein. -Funktionen werden immer mit dem keyword "def" definiert. Bei einer Funktion des Typs void (ohne Rückgabewert) braucht es kein zusätzliches Keyword. -Eine Funktion ist in Python folgendermassen aufgebaut: -""" - -###################################################################### - - -def funktion_name(arg1, arg2): - # body - return # can be omited - -###################################################################### - - -""" -Zwei Beispiele könnten die Folgenden sein: -""" - -###################################################################### - - -def print_hello(): - print("Hello") - - -def sum(i, j): - return i + j - - -# Call the defined functions -print_hello() -print(sum(1, 6)) - - -###################################################################### - -""" -Es können noch einige zusätziche Informationen bei der Definition angegeben werden, wie Beispielsweise -Ein Docstring (Beschreibung der Funktion), ein Return Datentyp und Datentypen der Parameter. -Die Funktionen von Oben könnten also wie folgt angepasst werden: -""" - -###################################################################### - - -def print_hello() -> None: - """Prints 'Hello' to the console""" - print("Hello") - - -def sum(i: int, j: int) -> int: - """Returns the sum of i and j""" - return i + j - - -###################################################################### -""" -Dies ist aber nur eine Information und hat keinen Einfluss auf das Programm oder den Compiler! -Der Funktionaufruf sum(1, "green") würde ausgeführt und das Programm einen Fehler werfen! -Daher ist es von Vorteil, Docstrings sowie Paremeter und Rückgabetypen anzugeben, um während dem Code Schreiben mögliche Fehler besser zu erkennen. -Durch hovern über die Methode sind diese Informationen ersichtlich. - -Eine Liste mit Datentypen in Python gibt es hier: https://www.w3schools.com/python/python_datatypes.asp -""" diff --git a/docs/06_oop/06_1_classes.ipynb b/docs/06_oop/06_1_classes.ipynb new file mode 100644 index 0000000..59c7d9f --- /dev/null +++ b/docs/06_oop/06_1_classes.ipynb @@ -0,0 +1,202 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Klassen\n", + "Während des Programmieren stellst du schnell fest, dass bestimmte Variablen zueinander gehören und dass oft ähnliche Operationen mit dem gleichen Variablen durchgeführt werden.\n", + "\n", + "\n", + "Nehmen wir das Beispiel eines Baumes. Nehmen wir an, dass ein Baum einen Namen/eine Art `species` sowie eine bestimmte Höhe `height` besitzt.\n", + "\n", + "Angenommen, wir hätten zwei Bäume. Ein Anfänger in Python könnte es ungefähr so versuchen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "species_oak = \"oak\"\n", + "species_pine = \"pine\"\n", + "\n", + "height_oak = 10\n", + "height_pine = 8\n", + "\n", + "def describe(species, height):\n", + " return f\"This tree is a {species} and is {height} m tall.\"\n", + "\n", + "\n", + "print( describe(species_oak, height_oak) )\n", + "print( describe(species_pine, height_pine) )" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel sehen wir aber schon, dass bestimmte Werte zum \"Gleichen\" dazugehören. Nämlich hat ein Baum jeweils eine `species` und eine `height`.\n", + "\n", + "Dass ein Baum immer diese Werte haben kann, können wir definieren. Das können wir mit einer Klassen-Definition erzielen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, species, height):\n", + " self.species = species\n", + " self.height = height\n", + "\n", + " # Methode\n", + " def describe(self):\n", + " return f\"This tree is a {self.species} and is {self.height} m tall.\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit dem Keyword `class` haben wir die neue Klasse `Tree` eingeleitet.\n", + "\n", + "Die Methode (=Funktion) `__init__(self, ...)` wird Konstruktor genannt. Das ist eine spezielle Methode, die immer aufgerufen wird, wenn ein neues Objekt von dieser Klasse erzeugt wird. Im folgenden Code erzeugen (\"initialisieren\") wir ein neues Objekt `oak` vom Typ `Tree`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "oak = Tree(\"oak\", 10)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hier haben wir ein neues Objekt vom Typ `Tree` erstellt und der Variable `oak` zugewiesen.\n", + "\n", + "Mit diesem Code `Tree(\"oak\", 10)` haben wir den Konstruktor aufgerufen und folgende Werte übergeben, die dann im Konstruktor verfügbar sind: `species: \"oak\", height: 10`.\n", + "\n", + "Das Argument `self` wird bei Python automatisch übersprungen bzw. automatisch übergeben. Dieses `self` ist im Prinzip eine Referenz auf sich selber.\n", + "\n", + "Mit `self.species = species` wird im Objekt der Klasse eine Variable `species` gespeichert. Wichtig zu erwähnen ist hierbei, dass das `species` ohne `self.` vorne dran ein völlig normales Argument aus dem Parameter des Konstruktors ist. Die beiden Variablen müssen auch nicht gleich heissen. Z.B. würde auch das funktionieren: `self.species = \"hello\"`.\n", + "\n", + "Möchte man innerhalb der Klasse auf Variablen zugreifen, die auf Objekt-Ebene gespeichert sind, dann muss `self.` vorne dran geschrieben werden, damit man auf das Objekt zugreifen kann.\n", + "\n", + "Auf diese Weise können also Variablen definiert werden, die im Objekt gespeichert werden. Dies ist übrigens eine bekannte Schreibweise, um zu definieren, welche Eigenschaften (also Variablen) eine Klasse besitzt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wir haben in der Klasse auch eine Funktion/Methode definiert: `describe()`.\n", + "\n", + "Diese können wir für ein Objekt aufrufen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "oak.describe()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Hinweis zu `self`**\n", + "\n", + "Dir ist sicherlich auch das `self` im Parameter der `describe(self)`-Methode aufgefallen. Das Argument `self` wird automatisch übergeben und ist hier automatisch das Objekt `oak`, weil das vor dem `.describe()` stand.\n", + "\n", + "Das `self` ist somit die Information, auf welchem Objekt die Methode aufgerufen wird." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zusammenfassung\n", + "Den Code ganz oben können wir mit Hilfe der Klasse wie folgt umformulieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, species, height):\n", + " self.species = species\n", + " self.height = height\n", + "\n", + " # Methode\n", + " def describe(self):\n", + " return f\"This tree is a {self.species} and is {self.height} m tall.\"\n", + "\n", + "\n", + "oak = Tree(\"oak\", 10)\n", + "pine = Tree(\"pine\", 8)\n", + "\n", + "print( oak.describe() )\n", + "print( pine.describe() )" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wie du hier sehen konntest, kann Programmieren mit Klassen (sogenanntes objekt-orientiertes Programmieren) stark dabei helfen, den Code \"wiederverwendbar\" zu machen.\n", + "\n", + "Das bedeutet z.B., dass wir automatisch eine Variable `species` und `height` bei unseren `Tree`-Objekten initialisieren.\n", + "\n", + "Ausserdem erzwingen wir automatisch, dass wir bei jedem solchen Objekt einen Wert für `species` und `height` angeben müssen (auch wenn er `None` sein kann). Das macht es für uns als Entwickler:innen einfacher, Fehler im Code früher zu bemerken, weil wir automatisch schon beim Erstellen des Objektes darauf aufmerksam gemacht werden, dass z.B. ein Wert vergessen gegangen ist.\n", + "\n", + "Ein weiterer Vorteil von objekt-orientierter Programmierung (=Verwendung von Klassen) ist es, dass der Code verständlicher, aufgeräumter und einfacher wartbar (wenn jemand anderes z.B. Änderungen daran vornehmen muss) wird." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/06_oop/06_2_methods_in_classes.ipynb b/docs/06_oop/06_2_methods_in_classes.ipynb new file mode 100644 index 0000000..7e5393b --- /dev/null +++ b/docs/06_oop/06_2_methods_in_classes.ipynb @@ -0,0 +1,279 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Magic Methods\n", + "In der vorherigen Einführung zu Klassen haben wir bereits eine Art kennengelernt, wie Methoden (=Funktionen) in Klassen definiert werden können.\n", + "\n", + "In diesem Kapitel möchten wir Dir einige sehr wichtige Standard-Methoden in Klassen näherbringen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unter \"magic methods\" oder \"dunder methods\" verstehen wir Methoden, die von Python aus vordefinierte Namen besitzen und automatisch unter bestimmten Umständen ausgeführt werden." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Konstruktor\n", + "Den Konstruktor hast du bereits bei der Einführung kennengelernt.\n", + "\n", + "Der Konstruktor muss den Namen `__init__` haben." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, species, height):\n", + " self.species = species\n", + " self.height = height\n", + "\n", + "\n", + "oak = Tree(\"oak\", 10)\n", + "\n", + "print(oak.species)\n", + "print(oak.height)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Den Konstruktor haben wir hier auf folgende Weise aufgerufen: `Tree(\"oak\", 10)`.\n", + "\n", + "Wir rufen den Konstruktor also auf, indem wir den Namen der Klasse angeben und dann in Klammern Werte für die Argumente mitgeben.\n", + "\n", + "Das Argument `self` muss nie manuell gesetzt werden. Der wird praktisch immer automatisch übergeben. Beim Konstruktor ist es das Objekt, das neu erstellt wird." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## To String\n", + "Wenn wir ein Objekt direkt mit `print(...)` ausgeben, dann möchten wir einen nützlichen Text sehen.\n", + "\n", + "Hierfür gibt es die `__str__`-Methode, die automatisch ein Objekt in einen String konvertiert. Siehe dieses Beispiel:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, species, height):\n", + " self.species = species\n", + " self.height = height\n", + "\n", + " # To String\n", + " def __str__(self):\n", + " return f\"This tree is a {self.species} and is {self.height} m tall.\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wenn wir nun ein Objekt dieses Types `print`en, dann wird automatisch der Rückgabewert dieser `__str__`-Methode verwendet:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "oak = Tree(\"oak\", 10)\n", + "\n", + "print( oak )" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchtest du ein Objekt in eine String-Variable packen, dann kannst du das mit der `str(...)`-Funktion tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "variable = str(oak)\n", + "\n", + "variable" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die `str(...)`-Funktion ruft automatisch die `__str__(self)`-Methode des Objektes auf." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## String Representation eines Objektes\n", + "\n", + "Sehr bekannt ist auch die Methode `__repr__(self)`. Sie erfüllt praktisch den gleichen Zweck wie `__str__`.\n", + "\n", + "Wenn sie nicht definiert ist, gibt sie (wie `__str__` auch) zurück, was für eine Klasse hinter dem Objekt steckt und wo im Arbeitsspeicher sie gespeichert ist:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(str(oak))\n", + "print(repr(oak))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die `__repr__`-Funktion wird eher im Debugging-Umfeld verwendet.\n", + "\n", + "Aber zuerst einmal ein Beispiel, wie sie definiert sein könnte:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, species, height):\n", + " self.species = species\n", + " self.height = height\n", + "\n", + " # To String\n", + " def __str__(self) -> str:\n", + " return f\"This tree is a {self.species} and is {self.height} m tall.\"\n", + " \n", + " # Repr\n", + " def __repr__(self) -> str:\n", + " return f\"Tree: species: {self.species}, height: {self.height}\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Versuche im folgenden Code einen Break-Point bei der ersten `print(...)`-Anweisung zu aktivieren und den den Code zu debuggen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pine = Tree(\"pine\", 8)\n", + "\n", + "print(str(pine))\n", + "print(repr(pine))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dann wirst du sehen, dass bei der Schnell-Ansicht des Debuggers für die Variable der Wert der `__repr__`-Methode angezeigt wird:\n", + "\n", + "![Debugging mit repr](./06_2_repr_debug.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Weitere Magic Methods\n", + "\n", + "Weitere solche Magic Methods sind hier aufgelistet: https://docs.python.org/3/reference/datamodel.html#special-method-names\n", + "\n", + "Hier noch eine Zusammenfassung von den bekanntesten. Du wirst keinen davon zwingend brauchen, ist aber gut zu wissen, was Du alles übersteuern kannst:\n", + "\n", + "* `__len__`: Gibt den Wert für den Aufruf von `len(...)`.\n", + "* Vergleichen von zwei Objekten:\n", + " * `__eq__`: Operator `==`.\n", + " * `__ne__`: Operator `!=`.\n", + " * `__lt__`: Operator `<`.\n", + " * `__gt__`: Operator `>`.\n", + " * `__le__`: Operator `<=`.\n", + " * `__ge__`: Operator `>=`.\n", + "* Mathematische Operationen:\n", + " * `__add__`: Operator `+`.\n", + " * `__sub__`: Operator `-`.\n", + " * `__mul__`: Operator `*`.\n", + " * `__div__`: Operator `/`.\n", + " * `__mod__`: Operator `%` (Modulo).\n", + " * `__pow__`: Operator `**` (Hochrechnen).\n", + "* Container Types:\n", + " * `__getitem__`: Zugriff auf ein Element mit [].\n", + " * `__setitem__`: Setzen eines Elements mit [].\n", + " * `__contains__`: Prüfen, ob ein anderes Element in diesem Objekt drin ist. Verwendet für den `in`-Operator.\n", + "* `with`-Statement:\n", + " * `__enter__`: Wird beim Betreten eines `with`-Blocks aufgerufen.\n", + " * `__exit__`: Wird beim Verlassen eines `with`-Blocks aufgerufen." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/06_oop/06_2_repr_debug.png b/docs/06_oop/06_2_repr_debug.png new file mode 100644 index 0000000..0fcb69a Binary files /dev/null and b/docs/06_oop/06_2_repr_debug.png differ diff --git a/docs/06_oop/06_3_types_of_methods.ipynb b/docs/06_oop/06_3_types_of_methods.ipynb new file mode 100644 index 0000000..5374688 --- /dev/null +++ b/docs/06_oop/06_3_types_of_methods.ipynb @@ -0,0 +1,290 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Methoden-Arten\n", + "\n", + "Im Zusammenhang mit Klassen solltest du zwischen diesen Arten von Methoden unterscheiden können:\n", + "* Instanz-Methoden (gewöhnliche Methoden)\n", + "* Statische Methoden (`@staticmethod` und `@classmethod`)\n", + "\n", + "In den aller meisten Fällen wirst du Instanz-Methoden verwenden, statische Methoden können aber manchmal sehr nützlich sein." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Instanz-Methoden\n", + "\n", + "Instanz-Methoden wurden vorher schon vorgestellt. Sie sind Methoden, die dann auf einer Instanz (also einem Objekt) ausgeführt werden.\n", + "\n", + "Für folgende Klasse definieren wir genau 1 neue Instanz-Methode (`talk`):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Instanz-Methode\n", + " def talk(self):\n", + " return \"Hello I'm a rubber tree and I'm just standing here a little, like this.\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchten wir nun diese Methode aufrufen, dann müssen wir zuerst eine Instanz (also ein Objekt) dieser Klasse erzeugen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rubber_tree = Tree()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Und anschliessend können wir die Methode auf der Instanz aufrufen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rubber_tree.talk()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Was hingegen nicht funktioniert, ist, die Methode direkt auf der Klasse auszuführen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Tree.talk()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Führst du den vorherigen Code aus, dann wird reklamiert, dass das Argument `self` nicht übergeben wurde...\n", + "\n", + "Rufst du die Methode auf einer Instanz (hier `rubber_tree`) auf, dann wird implizit die Instanz als erstes Argument übergeben (für den Parameter `self`).\n", + "\n", + "`rubber_tree.talk()` ruft im Prinzip das auf:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Tree.talk(rubber_tree)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Kurz zusammengefasst, eine Instanz-Methode\n", + "* ist immer mit einer Instanz (Objekt) verknüpft und muss daher auf einem Objekt (nicht der Klasse direkt) ausgeführt werden\n", + "* und hat immer `self` als erstes Argument im Parameter." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Statische Methoden\n", + "In bestimmten Fällen kommt es vor, dass du eine Methode in einer Klasse haben willst, du aber für den Aufruf der Methode noch kein Objekt der Klasse instantiiert haben willst oder kannst." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### @classmethod\n", + "\n", + "Ein bekanntes Beispiel für eine statische \"Klassen-Methode\" ist das instantiieren eines neues Objektes anhand eines speziellen Datentypes wie einem Dictionary. Im folgenden Beispiel erhalten wir ein Dictionary als Input und wandeln es in ein Objekt der Klasse `Tree` um:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, species, height):\n", + " self.species = species\n", + " self.height = height\n", + "\n", + " # Instanz-Methode\n", + " def describe(self):\n", + " return f\"This tree is a {self.species} and is {self.height} m tall.\"\n", + "\n", + " # Klassen-Methode\n", + " @classmethod\n", + " def from_dict(cls, my_dict: dict):\n", + " return cls(my_dict[\"species\"], my_dict[\"height\"])\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Und so rufen wir sie auf:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "my_input = {\n", + " \"species\": \"oak\",\n", + " \"height\": 10,\n", + "}\n", + "\n", + "oak = Tree.from_dict(my_input)\n", + "\n", + "oak.describe()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Der Fokus in diesem Beispiel liegt auf der statischen Klassen-Methode:\n", + "```python\n", + "@classmethod\n", + "def from_dict(cls, my_dict: dict):\n", + " return cls(my_dict[\"species\"], my_dict[\"height\"])\n", + "```\n", + "\n", + "Die Annotation `@classmethod` bewirkt, dass\n", + "* die Methode direkt auf der Klasse aufgerufen werden kann\n", + "* und dass das erste Argument `cls` automatisch übergeben wird.\n", + "\n", + "Das erste Argument `cls` ist ist kurz und steht für `class`. In diesem Beispiel ist `cls = Tree`.\n", + "\n", + "Die Anweisung\n", + "```python\n", + "cls(\"oak\", 10)\n", + "```\n", + "\n", + "ist im Prinzip das Gleiche wie\n", + "```python\n", + "Tree(\"oak\", 10)\n", + "```\n", + "\n", + "In diesem statischen Kontext hast du aber möglicherweise noch kein Zugriff auf die Klasse `Tree` und musst womöglich wie in diesem Beispiel über `@classmethod` und `cls` kehren." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### @staticmethod\n", + "\n", + "Mit `@staticmethod` kannst du statische Methoden definieren. Im Gegensatz zu `@classmethod` wirst du keine Referenz auf die Klasse selber benötigen. Deswegen entfällt der erste Parameter `cls`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # statische Methode (ohne cls)\n", + " @staticmethod\n", + " def random_quote() -> str:\n", + " # source of quote: unknown.\n", + " return \"Trees are nature's way of showing us that even when we're rooted, we can still branch out.\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Methode kannst du nun wie folgt aufrufen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Tree.random_quote()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/06_oop/06_4_variables_in_classes.ipynb b/docs/06_oop/06_4_variables_in_classes.ipynb new file mode 100644 index 0000000..7bb2f96 --- /dev/null +++ b/docs/06_oop/06_4_variables_in_classes.ipynb @@ -0,0 +1,346 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Variablen in Klassen\n", + "Klassen bieten sich an, um verschiedene Variablen zu gruppieren, die zusammengehören." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Statische Variablen\n", + "Hast du Variablen, die zusammengehören und nur einmal im Programm vorkommen, dann könntest du diese z.B. statisch in einer Klasse zusammenfassen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class DataBaseConfig:\n", + " DATABASE_URL: str = \"mysql://username:password@host:port/database_name\"\n", + " user_name: str\n", + " password: str" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Auf diese Variablen kannst du dann wie folgt zugreifen und sie manipulieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(DataBaseConfig.DATABASE_URL)\n", + "DataBaseConfig.user_name = \"admin\"\n", + "DataBaseConfig.password = \"hello123\"\n", + "\n", + "# Print out the top secret credentials :)\n", + "print(DataBaseConfig.user_name, DataBaseConfig.password)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Das ist bereits die Theorie, wie du statische Variablen in Klassen definieren kannst.\n", + "\n", + "Bitte merke dir, dass in Python trotzdem komisches Zeug funktioniert wie das:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "instance = DataBaseConfig()\n", + "\n", + "instance.user_name = \"tux\"\n", + "\n", + "print(instance.user_name)\n", + "print(DataBaseConfig.user_name)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wie du siehst, werden diese Variablen auch zusätzlich im Objekt neu erstellt.\n", + "\n", + "Dies ist nicht weiter schlimm, aber denke daran, dass das nicht der Zweck von statischen Variablen sein sollte. Diese Variablen solltest du besser nicht auf dem Objekt anschauen oder manipulieren, weil die Variablen nicht für diesen Zweck definiert worden sind." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Instanz-Variablen\n", + "\n", + "Oft gruppierst du Variablen in Klassen, die mehrmals verwendet werden sollen.\n", + "\n", + "Z.B. dein geliebtes Beispiel der Baum-Klasse `Tree`.\n", + "\n", + "Möglicherweise möchtest du mehrere Bäume instantiieren und die Variable `height` z.B. für jeden Baum anders setzen. Statisch macht die Variable `height` aber keinen Sinn.\n", + "\n", + "Normalerweise werden Instanz-Variablen im Konstruktor definiert, indem sie auf der Variable `self` zugewiesen werden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, my_species, my_height):\n", + " self.species = my_species\n", + " self.height = my_height" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Verwenden Kannst du die Variablen dann wie folgt:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pine = Tree(\"pine\", 8)\n", + "\n", + "print(pine.species) # lesen\n", + "print(pine.height)\n", + "\n", + "\n", + "# schreiben:\n", + "pine.height = 9\n", + "pine.species = \"pine tree\"\n", + "\n", + "print(pine.species) # lesen\n", + "print(pine.height)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Datenklassen\n", + "Sehr oft hast du Klassen, die nur dazu dienen, Daten zu speichern und keine statischen Variablen besitzen.\n", + "\n", + "In diesem Fall kannst du dir den Konstruktor sparen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "\n", + "@dataclass\n", + "class Tree:\n", + " # Instanz-Variablen\n", + " species: str\n", + " height: float" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Verwendest du die `@dataclass`-Annotation, dann\n", + "* kannst du die Variablen ohne schlechtes Gewissen direkt in die Klasse reinschreiben\n", + "* und der Konstruktor wird für dich automatisch generiert.\n", + "\n", + "Die Klasse kannst du dann wie folgt verwenden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sausage_tree = Tree(\"Sausage Tree (Kigelia africana)\", 15)\n", + "\n", + "print(sausage_tree)\n", + "\n", + "sausage_tree.height = 20\n", + "\n", + "print(sausage_tree)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Verwendung von `@dataclass` bringt dir u.A. folgende Vorteile:\n", + "* Konstruktor, `__str__` und `__repr__` werden automatisch implementiert.\n", + "* Datenklassen sind oft lesbarer.\n", + "* Variablen können als immutable (unveränderbar) definiert werden. (Verwende `@dataclass(frozen=True)`)\n", + "* Helper-Funktionen wie z.B. `asdict(...)`.\n", + "\n", + "Der letzte Punkt möchte deutlich machen, dass viele hilfreiche Funktionen bei Datenklassen automatisch implementiert wurden. Möchtest du z.B. das Objekt in ein Dictionary umwandeln, dann kannst du das mit der `asdict(...)`-Funktion tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import asdict\n", + "\n", + "\n", + "sausage_tree = Tree(\"Sausage Tree (Kigelia africana)\", 15)\n", + "\n", + "tree_as_dict: dict = asdict(sausage_tree)\n", + "\n", + "print(tree_as_dict)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die `@dataclass`-Annotation vereinfacht es sehr stark, Klassen zum Speichern von Variablen zu erstellen.\n", + "\n", + "Trotzdem fehlen bei `@dataclass`s viele Funktionen, weshalb in der Praxis sehr oft das Package \"pydantic\" verwendet wird.\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sichtbarkeit von Variablen und Methoden\n", + "Oftmals besitzen Klassen Variablen, die sichtbar sein sollen und solche, die dem Anwender der Klasse nicht ersichtlich sein sollte.\n", + "\n", + "Es ist eine Good-Practice, Variablen und Methoden zu verstecken, die nicht ausserhalb der Klasse verwendet werden sollten.\n", + "\n", + "Im folgenden Beispiel wird in der Klasse zusätzlich gespeichert, wann dass der Baum gepflanzt (erstellt) wurde:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "\n", + "\n", + "class Tree:\n", + " # Konstruktor\n", + " def __init__(self, my_species, my_height):\n", + " self.species = my_species\n", + " self.height = my_height\n", + " self.__planted = datetime.now() # private Variable.\n", + " \n", + " def get_date_planted(self) -> datetime:\n", + " return self.__planted" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Variable `__planted` beginnt mit 2 Underscores, damit klar ist, dass die Variable private ist und man folglich nicht ausserhalb der Klasse darauf zugreifen soll.\n", + "\n", + "In der Klasse selber kann und darf ohne Einschränkungen darauf zugegriffen werden. Die Methode `get_date_planted()` gibt diesen Wert zurück:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sausage_tree = Tree(\"Sausage Tree (Kigelia africana)\", 15)\n", + "\n", + "sausage_tree.get_date_planted()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Was hingegen vermieden werden soll (und teilweise automatisch einen Fehler wirft), ist, ausserhalb der Klasse auf solche Variablen mit `__` am Anfang des Namens zuzugreifen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sausage_tree.__planted" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wie wir hier sehen, definieren diese Underscores die Sichtbarkeit der Variable. Das Gleiche gilt auch für Methoden.\n", + "\n", + "Folgendes sind die wichtigsten Sichtbarkeitsstufen:\n", + "* public (ohne Underscores am Anfang): Dieses Feld ist immer sichtbar.\n", + "* protected, beginnend mit 1 Underscore (`_`): Dieses Feld ist nur in der Klasse und deren Subklassen (folgt in einem späteren Kapitel) sichtbar, aber nicht von aussen.\n", + "* private, beginnend mit 2 Underscores (`__`): Dieses Feld ist nur in der Klasse sichtbar, von aussen nie." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/06_oop/06_5_pydantic.ipynb b/docs/06_oop/06_5_pydantic.ipynb new file mode 100644 index 0000000..44f3eae --- /dev/null +++ b/docs/06_oop/06_5_pydantic.ipynb @@ -0,0 +1,369 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# pydantic\n", + "Im vorherigen Kapitel haben wir `@dataclass`es angeschaut. Diese haben eine kurze und sehr praktische Möglichkeit angeboten, Klassen für die Speicherung von Daten zu erstellen.\n", + "\n", + "Im Zusammenhang mit Daten müssen oft weitere Dinge gemacht werden, die sich immer wieder wiederholen:\n", + "* Die Struktur einer Klasse definieren (welche Variablen hat die Klasse).\n", + "* Daten (Werte) überprüfen, z.B. anhand des Datentypes.\n", + "* (De)Serialization: Daten umwandeln von oder nach JSON, XML oder YAML.\n", + "\n", + "Pydantic widmet sich genau diesen Themen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pydantic installieren" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pydantic ist nicht in der Standard-Installation von Python enthalten und muss daher zuerst heruntergeladen werden.\n", + "\n", + "Um es zu installieren, füge das Package `pydantic` zu deinen Dependencies hinzu." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Verwendest du bereits Poetry (wird im Kapitel \"project_structure\" später erläutert), dann kannst du das mit folgendem Kommandozeilenbefehl tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!poetry add pydantic" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Verwendest du kein Poetry, dann ist eine Good-Practice, die verwendeten Dependencies in einer Datei zu notieren, damit andere Entwickler wissen, welche Dependencies/Packages sie auch benötigen.\n", + "\n", + "Solche Dependencies werden oft in der Datei `requirements.txt` gespeichert, wobei jede Zeile der Name der Dependency ist (kann auch die Version enthalten).\n", + "\n", + "In diesem Fall macht es Sinn, nicht die Dependency alleine zu installieren, sondern so, wie sie in der Dependency-Datei definiert ist. Das kann mit dem Befehl `pip install -r ` (`-r` für \"requirements\") erreicht werden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! echo pydantic > ./requirements.txt\n", + "\n", + "! pip install -r ./requirements.txt" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Bist du hingegen ein kleiner Erfinder und unstrukturierter Bastler, dann reicht folgender Kommandozeilenbefehl aus:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! pip install pydantic" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Datenklasse erstellen\n", + "\n", + "Eine neue Datenklasse kann ziemlich ähnlich mit pydantic erstellt werden wie mit der `@dataclass`-Annotation. Zu Beachten ist, dass die neue Datenklasse (auch Model genannt) von der pydantic-Klasse `BaseModel` erben muss, sprich nach dem Namen der Klasse muss in Klammern `BaseModel` angegeben werden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pydantic import BaseModel\n", + "\n", + "\n", + "class Tree(BaseModel):\n", + " # Instanz-Variablen\n", + " species: str\n", + " height: float\n", + "\n", + " def describe(self):\n", + " return f\"This tree is a {self.species} and is {self.height} m tall.\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Klasse kann dan ziemlich ähnlich wie eine `@dataclass`-Klasse verwendet werden.\n", + "\n", + "Beachte, dass beim Konstruktor die Namen der Argumente angegeben werden muss:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dracula_tree = Tree(species=\"Dracula Orchid\", height=0.2)\n", + "\n", + "dracula_tree.describe()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Validierung\n", + "Pydantic wird oft auch dafür verwendet, um Python mit Features zu versehen, die typisierte Programmiersprachen oft schon mitbringen: Prüfen, ob der Datentyp bei einer Zuweisung stimmt.\n", + "\n", + "Bei unserem Model (=Datenklasse) haben wir angegeben, dass die `height` vom Typ `float` ist. Pydantic berücksichtigt diese Information und wirft einen Fehler, wenn etwas anderes als eine Zahl versucht wird, zuzuweisen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dracula_tree = Tree(species=\"Baobab Tree\", height=\"twenty\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zusätzlich ist es möglich, weitere Bedingungen an die Variablen zu knüpfen, bspw. dass die Höhe grösser als `0` sein muss. Hierfür weisen wir dem Feld den Rückgabewert der Funktion `Field(...)` zu:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pydantic import BaseModel, Field\n", + "\n", + "class Tree(BaseModel):\n", + " species: str = Field(..., description=\"The species of the tree.\")\n", + " height: float = Field(..., gt=0, description=\"The height of the tree in meters.\")\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Das `description`-Argument hat keine Funkion. Sie wird oft dafür verwendet, um den Sinn und die Verwendung der Variable zu dokumentieren, damit ein andere(r) Entwickler:in versteht, wofür die Variable verwendet wird.\n", + "\n", + "Für die Variable `height` wurde das Argument `gt` spezifiziert. Dies steht für \"greater than\" (also grösser als). Mit diesem optionalen Argument können wir festlegen, dass keine Werte unter 0 akzeptiert werden.\n", + "\n", + "Folglich sollte folgendes funktionieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pancake_tree = Tree(species=\"Pancake Tree\", height=13)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Und folgendes natürlich nicht:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dwarf_willow_tree = Tree(species=\"Dwarf Willow\", height=-0.02)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Neben `gt` gibt es viele weitere Parameter, die hilfreich im Bezug auf Validierung sind. Hier sind einige aufgelistet:\n", + "* `gt`: grösser als.\n", + "* `ge`: Grösser oder gleich.\n", + "* `lt`: Kleiner als.\n", + "* `le`: Kleiner oder gleich.\n", + "* `ne`: ungleich.\n", + "* `anystr_length`: Länge eines Strings.\n", + "* `regex`: Regex, der die Variable \"matchen\" muss.\n", + "* `email`: Muss eine Email sein.\n", + "* `url`: Muss eine URL sein.\n", + "* `positive`: Muss eine positive Zahl sein.\n", + "* `negative`: Muss eine negative Zahl sein.\n", + "* `none`: Nur `None` ist als Wert zulässig.\n", + "\n", + "Hier findest du mehr Parameter und Beispiele: https://docs.pydantic.dev/latest/usage/schema/#field-customization-parameters" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Serialization\n", + "\n", + "Unter diesem Begriff verstehen wir das Umwandeln vom Objekt in ein \"serialisiertes\" Format wie JSON, XML oder YAML. Pydantic unterstützt das Konvertieren in JSON.\n", + "\n", + "Nehmen wir als Beispiel folgendes als Objekt:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "corkscrew_tree = Tree(species=\"Corkscrew Willow\", height=7)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Möchten wir das Objekt als Dictionary präsentiert haben, dann bekommen wir dies mit der `json()`-Methode:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tree_as_dict = corkscrew_tree.dict()\n", + "tree_as_dict" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wenn wir es direkt als JSON-String haben möchten, dann reicht dies aus:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tree_as_json = corkscrew_tree.json()\n", + "tree_as_json" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deserialization\n", + "Deserialization ist Serialization in die andere Richtung:\n", + "\n", + "Wir haben z.B. einen JSON-String und möchten daraus ein Objekt instantiieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "json_string = '{\"species\": \"Elephant Foot Yam Tree\", \"height\": 2.5}'\n", + "\n", + "elephant_tree = Tree.parse_raw(json_string)\n", + "\n", + "elephant_tree" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Und wenn wir von einem Dictionary aus deserialisieren möchten:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dictionary = {\"species\": \"Tickle-Me-Not Tree\", \"height\": 1.5}\n", + "\n", + "tickle_me_not_tree = Tree.parse_obj(dictionary)\n", + "\n", + "tickle_me_not_tree" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/06_oop/06_6_inheritance.ipynb b/docs/06_oop/06_6_inheritance.ipynb new file mode 100644 index 0000000..f6a1678 --- /dev/null +++ b/docs/06_oop/06_6_inheritance.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vererbung\n", + "\n", + "Vererbung erlaubt es, neue Klassen auf Grundlage anderer zu erstellen.\n", + "\n", + "Die neue Klasse wird oft _Subklasse_ oder _Child-Klasse_ genannt, während die alte Klasse, von welcher \"geerbt\" wird, _Parent_- oder _Base-Klasse_ genannt wird.\n", + "\n", + "Nehmen wir das Beispiel von `User`n:\n", + "* Alle User haben einen `username`.\n", + "* Alle User haben ein Profil, das man anschauen kann.\n", + "\n", + "Als Klasse könnte `User` so aussehen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "\n", + "\n", + "@dataclass\n", + "class User:\n", + " username: str\n", + "\n", + " def show_profile(self):\n", + " print(f\"This is User {self.username}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Admin`s sind auch `User`, besitzen alle Eigenschaften von `User`n und können noch mehr:\n", + "* Sie können andere User verwalten.\n", + "\n", + "Dass `Admin`s alle Eigenschaften von `User` erben soll, kann erreicht werden, indem die Parent-Klasse `User` in Klammern neben dem neuen Klassennamen hingeschrieben wird:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Admin(User):\n", + " def manage_user(self, other: User, new_username):\n", + " other.username = new_username" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nun können wir Objekte von der Klasse `User` und `Admin` erstellen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# User (kein Admin):\n", + "user123 = User(\"user123\")\n", + "pierre: User = User(\"pièrre\")\n", + "\n", + "# Admins:\n", + "tux = Admin(\"tux\")\n", + "root: User = Admin(\"root\")\n", + "chuck_norris: Admin = Admin(\"chuck_norris\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Auf allen diesen Objekten können wir das Profil anschauen, da dies bei allen `User`n möglich ist. Die `Admin`s besitzen auch diese Methode, weil die Klasse die Methode von `User` geerbt hat:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "user123.show_profile()\n", + "pierre.show_profile()\n", + "\n", + "# Admins:\n", + "tux.show_profile()\n", + "root.show_profile()\n", + "chuck_norris.show_profile()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die `manage_user(...)`-Medhode hingegen existiert nur für `Admin`s:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tux.manage_user(user123, \"user1\")\n", + "root.manage_user(user123, \"user2\")\n", + "chuck_norris.manage_user(user123, \"user3\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Das Konzept der Vererbung folgt dem Prinzip der Wiederverwendung von Code. Das bedeutet, dass du nicht für alle Subklassen die gleichen Methoden selber neu implementieren musst, sondern, dass es reicht, dass du sie in der Base-Klasse implementierst." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## \"Is-A\"-Beziehung\n", + "\n", + "Beim Definieren der Variable `root` ist dir sicher aufgefallen, dass wir ein `Admin`-Objekt einer `User`-Variable zugewiesen haben:\n", + "\n", + "```python\n", + "root: User = Admin(\"root\")\n", + "```\n", + "\n", + "Zu Beginn haben wir gesagt, dass die Variable vom Typ `User` sein soll: `root: User`.\n", + "\n", + "Rechts nach dem `=`-Operator folgt dann aber ein Objekt vom Typ `Admin`. Dies ist zulässig, weil ein `Admin` auch ein `User` ist (\"ist-ein\"-Beziehung), umgekehrt aber nicht zwingend.\n", + "\n", + "Möchten wir herausfinden, ob ein Objekt eine Instanz von einer bestimmten Klasse ist, dann verwenden wir die `isinstance(object, class)`-Funktion:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Prüfen, ob Objekt vom Typ `User` ist:\n", + "print( isinstance(user123, User) )\n", + "print( isinstance(pierre, User) )\n", + "print( isinstance(tux, User) )\n", + "print( isinstance(root, User) )\n", + "print( isinstance(chuck_norris, User) )\n", + "\n", + "print(\"\\n\")\n", + "\n", + "# Prüfen, ob User Admin ist:\n", + "print( isinstance(user123, Admin) )\n", + "print( isinstance(pierre, Admin) )\n", + "print( isinstance(tux, Admin) )\n", + "print( isinstance(root, Admin) )\n", + "print( isinstance(chuck_norris, Admin) )" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Diese Eigenschaft kann in sich als sehr nützlich herausstellen.\n", + "\n", + "Z.B. wenn wir eine Funktion schreibt, die etwas mit Objekten macht, die entweder vom Typ `User` oder `Admin` sind:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_all_usernames(users: list[User]) -> list[str]:\n", + " usernames = [user.username for user in users]\n", + "\n", + " return usernames\n", + "\n", + "\n", + "get_all_usernames([user123, pierre, tux, root, chuck_norris])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wie du gesehen hast, werden auch Admins in der Liste im Parameter akzeptiert.\n", + "\n", + "Folglich reicht es vollkommen aus, diese Methode 1-mal zu implementieren. Zusätzlich wird sie für alle weiteren Sub-Klassen funktionieren, die in der Zukunft geschrieben wird.\n", + "\n", + "Mit Vererbung können wir in vielen Fällen sehr viel Zeilen Code sparen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Methoden überschreiben\n", + "Oft möchtest du eine Methode in einer Sub-Klasse ein bisschen abändern. Das kannst du machen, indem du die genau gleiche Methode in der Sub-Klasse neu definierst (hier `get_privileges()`):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "from typing_extensions import override\n", + "\n", + "\n", + "@dataclass\n", + "class User:\n", + " username: str\n", + "\n", + " def get_privileges(self):\n", + " return [\"read\"]\n", + "\n", + "\n", + "class Admin(User):\n", + " @override\n", + " def get_privileges(self):\n", + " return [\"read\", \"write\"]\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wenn du nun `get_privileges()` auf einem `User`-Objekt aufrufst, dann wird die Methode in `User` aufgerufen.\n", + "\n", + "Da du diese Methode in `Admin` überschrieben hast, wird die neue Methode aufgerufen, wenn du die Methode im `Admin`-Objekt aufrufst:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "user123 = User(\"user123\")\n", + "root: User = Admin(\"root\")\n", + "\n", + "print( user123.get_privileges() )\n", + "print( root.get_privileges() )\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Beachte, dass die `@override`-Annotation freiwillig ist. Es ist aber eine Good-Practice, diese Angabe zu machen, damit andere Entwickler:innen sehen, dass die Methode überschrieben wird.\n", + "\n", + "Möchtest du, dass in deiner neuen Methode auch die überschriebene Methode aufgerufen wird, dann kannst du in der neuen Methode den Aufruf mit `super.Methoden-Name(Argumente)` ergänzen. In unserem Beispiel müsste das `super().get_privileges()` sein." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## protected\n", + "\n", + "In der Theorie zu Variablen in Klassen wurden verschiedene Sichtbarkeitstufen vorgestellt (public, protected und private).\n", + "\n", + "Nun stellen wir dir noch die _protected_-Sichtbarkeitsstufe vor. Methodennamen mit der Sichtbarkeitsstufe _protected_ beginnen mit 1 Underscore (`_`):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass\n", + "\n", + "\n", + "@dataclass\n", + "class User:\n", + " _username: str\n", + "\n", + " def get_username(self):\n", + " return self._username\n", + "\n", + "\n", + "class Admin(User):\n", + " def username_in_capital_letters(self):\n", + " return self._username.upper()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Protected_ bedeutet in diesem Fall, dass auch die Sub-Klassen (hier `Admin`) die Variable `_username` sehen kann:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "root = Admin(\"root\")\n", + "\n", + "print (root.get_username() )\n", + "print (root.username_in_capital_letters() )" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Meinung hinter der Sichtbarkeitsstufe _protected_ ist, dass nur die Parent- und Sub-Klasse die Variable `_username` sehen können sollen.\n", + "\n", + "Folglich sollte folgender Code vermieden werden, obwohl er funktioniert:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "root._username" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wichtig zu verstehen ist, dass die Sichtbarkeitsstufe _protected_ nicht wirklich eine Python-Funktion ist, sondern, dass sich die Entwickler:innen daran halten sollen, keine _protected_ Felder aufzurufen.\n", + "\n", + "Wenn du also in deinem Code eine Methode oder eine Variable von einem Objekt verwendest, die mit einem Underscore beginnt, dann verletzt du dieses Prinzip. Ausser: du befindest dich in der gleichen Klasse, in welcher die Variable definiert wird, oder in einer Sub-Klasse von ihr." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/075Imports.ipynb b/docs/075Imports.ipynb new file mode 100644 index 0000000..1a30c06 --- /dev/null +++ b/docs/075Imports.ipynb @@ -0,0 +1,87 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wie in Java können auch in Python externe Libraries, sowie auch eingene Files (Klassen und Methoden) in ein Skript eingebunden werden.\n", + "Dies ermöglicht eine Wiederverwendbarkeit und kann helfen, den Code, durch Auslagern von Elementen sauber zu halten.\n", + "Nachfolgend eineige Beispiele:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Importieren von Standardmodulen wie \"random\" oder \"os\"\n", + "import random\n", + "import os\n", + "\n", + "# Aufruf von Funktionen aus den importierten Modulen\n", + "rand_num = random.randint(0, 10)\n", + "cwd = os.getcwd()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel haben wir zwei Standardmodule `random` und `os` importiert und können nun Funktionen aus diesen Modulen in unserem Skript aufrufen. In diesem Fall haben wir die Funktion `randint` aus dem Modul `random` verwendet, um eine zufällige Ganzzahl zu generieren, und die Funktion `getcwd` aus dem Modul `os`, um den aktuellen Arbeitsverzeichnis zu erhalten." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Importieren von selbst erstellten Modulen wie \"my_module.py\"\n", + "import my_module\n", + "\n", + "# Aufruf von Funktionen aus dem importierten Modul\n", + "result = my_module.add(2, 3)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel haben wir ein benutzerdefiniertes Modul `my_module.py` erstellt, das eine Funktion `add` enthält, die die Summe zweier Zahlen berechnet. Wir haben dann dieses Modul in unserem Skript importiert und können nun die Funktion `add` aus diesem Modul aufrufen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Importieren einer spezifischen Funktion aus einem selbst erstellten Modul\n", + "from my_module import multiply\n", + "\n", + "# Aufruf der importierten Funktion\n", + "result = multiply(2, 3)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In diesem Beispiel haben wir eine spezifische Funktion `multiply` aus unserem benutzerdefinierten Modul `my_module.py` importiert und können nun diese Funktion direkt in unserem Skript aufrufen, ohne den Namen des Moduls vor der Funktion zu verwenden." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/07_project_structure/07_1_poetry.ipynb b/docs/07_project_structure/07_1_poetry.ipynb new file mode 100644 index 0000000..284c2b8 --- /dev/null +++ b/docs/07_project_structure/07_1_poetry.ipynb @@ -0,0 +1,84 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Poetry\n", + "\n", + "Python setzt keine bestimmte Ordnerstruktur voraus. Zusätzlich ist die Angabe verwendeten Dependencies (Modulen/Packages) keine Pflicht und auch nicht standardisiert.\n", + "\n", + "Hier kommt Poetry ins Spiel: Poetry adressiert diese Themen als All-in-One-System, um Python-Projekte zu managen.\n", + "Ähnlich wie NPM, Angular, Spring (initializr) oder Dart/Flutter bietet Poetry ein Tool an, mit welchem du ein neues Projekt generieren kannst." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Poetry installieren\n", + "\n", + "Die Installation ist hier beschrieben: https://python-poetry.org/docs/#installing-with-the-official-installer\n", + "\n", + "Zusammengefasst, kannst du es wie folgt installieren unter Windows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "powershell" + } + }, + "outputs": [], + "source": [ + "(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unter Linux und macOS kannst du es mit folgendem Befehl installieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!curl -sSL https://install.python-poetry.org | python3 -\n", + "\n", + "# oder unter macOS, wenn das erste nicht funktioniert:\n", + "# brew install poetry" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/07_project_structure/07_2_new_project.ipynb b/docs/07_project_structure/07_2_new_project.ipynb new file mode 100644 index 0000000..3ce5cfb --- /dev/null +++ b/docs/07_project_structure/07_2_new_project.ipynb @@ -0,0 +1,98 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neues Projekt aufsetzen" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die Projekt-Struktur variiert von Projekt zu Projekt teilweise sehr.\n", + "\n", + "Wenn du ein neues Projekt mit Poetry erstellen lässt, dann wird eine Projekt-Struktur erstellt, die sehr an diejenige anderer Programmiersprache erinnert.\n", + "\n", + "Um ein neues Projekt zu generieren, gib bitte folgendes in der Konsole ein, wobei du natürlich den Projektnamen deines neuen Projekts eingibst:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!poetry new --src " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dieser Befehl hat dir folgende Ordner-Struktur erstellt:\n", + "\n", + "```\n", + "\n", + "├── pyproject.toml\n", + "├── README.md\n", + "├── src\n", + "│ └── \n", + "│ └── __init__.py\n", + "└── tests\n", + " └── __init__.py\n", + "```\n", + "\n", + "Interessant ist, welche Ordner der Befehl erstellt hat:\n", + "* src/: Hier schreibst du deinen eigentlichen Code rein.\n", + "* tests: In diesem Ordner kannst du (Unit-)Tests schreiben.\n", + "\n", + "Was aber zentral für ein Poetry-Projekt ist, ist, dass es eine Datei `pyproject.toml` erstellt hat.\n", + "* In dieser Datei konfigurierst du das Projekt\n", + "* und gibst an, welche externen Packages (Dependencies) dein Projekt benötigt.\n", + "\n", + "Schau dir am besten gleich diese Datei an:\n", + "\n", + "```ini\n", + "[tool.poetry]\n", + "name = \"\"\n", + "version = \"0.1.0\"\n", + "description = \"\"\n", + "authors = [\"Vorname Nachname \"]\n", + "readme = \"README.md\"\n", + "packages = [{include = \"demo\", from = \"src\"}]\n", + "\n", + "[tool.poetry.dependencies]\n", + "python = \"^3.11\"\n", + "\n", + "\n", + "[build-system]\n", + "requires = [\"poetry-core\"]\n", + "build-backend = \"poetry.core.masonry.api\"\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Eine detailliertere Anleitung findest du hier:\n", + "\n", + "https://python-poetry.org/docs/basic-usage/" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/07_project_structure/07_3_dependencies.ipynb b/docs/07_project_structure/07_3_dependencies.ipynb new file mode 100644 index 0000000..efc7e81 --- /dev/null +++ b/docs/07_project_structure/07_3_dependencies.ipynb @@ -0,0 +1,115 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Dependencies\n", + "Benötigt dein Projekt eine Abhängigkeit (Dependency/ein anderes Python-Päcklein/-Bibliotheke), dann kannst du diese mit folgendem Befehl hinzufügen. Im folgenden Beispiel fügen wir das Python-Päcklein \"requests\" hinzu:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!poetry add requests" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dieser Befehl lädt die Dependency herunter und spezifiziert diese Abhängigkeit (inkl. der Version) in der Datei `pyproject.toml`:\n", + "\n", + "```ini\n", + "[tool.poetry.dependencies]\n", + "python = \"^3.11\"\n", + "requests = \"^2.30.0\"\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Von nun an kannst du das Package `requests` verwenden, aber nur in deinem Projekt! Diese Dependency ist nur in deinem Projekt verfügbar, weil Poetry automatisch ein sogenanntes \"virtual environment\" erstellt und die Dependencies dort drin installiert. Ein Virtual Environment kannst du dir so vorstellen, dass eine Kopie der aktuellen Python-Version gemacht wird und zusammen mit den Projekt-Dependencies in einen Ordner kopiert wird. Das hat den Vorteil, dass du für jedes Projekt eine eigene Python-Version und verschiedene Dependency-Versionen besitzen kannst.\n", + "\n", + "Das kannst du gleich testen, ob das installieren geklappt hat, indem du versuchst, dein Projekt auszuführen.\n", + "\n", + "Hierfür kannst du mal versuchen, folgendes in `/src//__init__.py` einzugeben:\n", + "\n", + "```python\n", + "print(\"hello world\")\n", + "\n", + "r = requests.get('https://google.ch')\n", + "print(r.status_code)\n", + "print(r.content)\n", + "```\n", + "\n", + "Den Code kannst du dann mit folgendem Befehl ausführen:\n", + "```shell\n", + "poetry run python3 src//__init__.py\n", + "```\n", + "\n", + "Nun solltest du in der Konsole folgendes sehen:\n", + "```\n", + "hello world\n", + "200\n", + "```\n", + "\n", + "und der Seiten-Code (HTML) von google.ch." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bereits spezifizierte Dependencies installieren\n", + "\n", + "Um die Dependencies zu installieren, die im pyproject.toml spezifiziert sind, kannst du wie folgt tun:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!poetry install" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dependencies werden nicht von VS Code erkannt?\n", + "\n", + "Sollte der `import` bei dir unterstrichen sein mit der Meldung, dass es diese Dependency nicht kennt, dann musst du bei VS Code womöglich noch den Python-Interpreter ändern.\n", + "\n", + "Das kannst du wie folgt tun:\n", + "\n", + "![Interpreter ändern](./poetry_configure_python_interpreter.png)\n", + "\n", + "* Klicke bei VS Code ganz unten auf die Python-Version.\n", + "* Wähle nun die Python-Version aus, die das Projekt verwendet. Achte dabei darauf, dass es diejenige verwendet, die sich in deinem \"virtualenv\" befindet. In diesem Fall war es diese Version, die \"virtualenvs\" im Dateipfad hatte (bzw. der Name eine zufällige Textfolge besitzt).\n", + "\n", + "Nun sollte auch VS Code deinen `import` korrekt erkennen." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/07_project_structure/07_4_run_project.ipynb b/docs/07_project_structure/07_4_run_project.ipynb new file mode 100644 index 0000000..e964ea5 --- /dev/null +++ b/docs/07_project_structure/07_4_run_project.ipynb @@ -0,0 +1,68 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Projekt ausführen\n", + "\n", + "Dein Projekt kannst du z.B. wie folgt ausführen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!poetry run python3 src//__init__.py" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Projekt mit VS Code debuggen\n", + "\n", + "In VS Code kannst du dein Projekt so konfigurieren, dass es automatisch (im Debugging-Modus) ausgeführt wird, wenn du die [F5]-Taste klickst.\n", + "\n", + "* Hierfür klickst du im \"Run and Debug\"-Tab auf \"create a launch.json file\".\n", + "\n", + "![Interpreter ändern](./2_vscode_create_launch_json.png)\n", + "\n", + "* anschliessend wählst du eines der ersten beiden Elemente aus (vorzugsweise Module):\n", + "\n", + "![Interpreter ändern](./3_vscode_create_python_launch.png)\n", + "\n", + "\n", + "Das generiert die ungefähr folgenden Code in der Datei `.vscode/launch.json`:\n", + "```json\n", + "{\n", + " \"version\": \"0.2.0\",\n", + " \"configurations\": [\n", + " {\n", + " \"name\": \"Python: Poetry\",\n", + " \"type\": \"python\",\n", + " \"request\": \"launch\",\n", + " \"module\": \"demo\",\n", + " \"justMyCode\": true\n", + " }\n", + " ]\n", + "}\n", + "```\n", + "\n", + "Nun startet dein Projekt, wenn du [F5] auf deiner Tastatur klickst." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/07_project_structure/2_vscode_create_launch_json.png b/docs/07_project_structure/2_vscode_create_launch_json.png new file mode 100644 index 0000000..2183462 Binary files /dev/null and b/docs/07_project_structure/2_vscode_create_launch_json.png differ diff --git a/docs/07_project_structure/3_vscode_create_python_launch.png b/docs/07_project_structure/3_vscode_create_python_launch.png new file mode 100644 index 0000000..eb41fa1 Binary files /dev/null and b/docs/07_project_structure/3_vscode_create_python_launch.png differ diff --git a/docs/07_project_structure/poetry_configure_python_interpreter.png b/docs/07_project_structure/poetry_configure_python_interpreter.png new file mode 100644 index 0000000..deb9dda Binary files /dev/null and b/docs/07_project_structure/poetry_configure_python_interpreter.png differ diff --git a/docs/080Dictionaries.py b/docs/080Dictionaries.py deleted file mode 100644 index d00e6d9..0000000 --- a/docs/080Dictionaries.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Dictionaries funktionieren ähnlich wie Arrays (bzw. Listen). Der Zugriff auf den Inhalt erfolgt aber mit Schlüsseln (sogenannte Keys) und nicht mit indexen. -Im Folgenden Beispiel wird ein Dictionary mit email-addressen von Personen erstellt. -""" - -###################################################################### - -email_database = {} # initialize empty dictionary - -# add a new entry with key "John" and value "John.Guildmore@gmail.com" -email_database["John"] = "John.Guildmore@gmail.com" - -email_database["Jack"] = "jack_reacher@hotmail.com" - -print(email_database["John"]) # John.Guildmore@gmail.com -print(email_database) -# {'John': 'John.Guildmore@gmail.com', 'Jack': 'jack_reacher@hotmail.com'} - - -###################################################################### - -""" -Natürlich kann man dasselbe Dictionary auch direkt befüllt initialisieren: -""" - -###################################################################### - -email_database = { - "John": "John.Guildmore@gmail.com", - "Jack": "jack_reacher@hotmail.com" -} - -###################################################################### - -""" -Einige wichtige Operationen mit Dictionaries sind: -""" - -###################################################################### - -dict_name = {} # Leeres Dictionary erzeugen - -dict_name["Key"] = "value" # Einfügen bzw. ändern eines Elements -dict_name.update({"key": "value"}) # Einfügen bzw. ändern eines Elements -del dict_name["Key"] # Eintrag löschen -dict_name.pop("key") # Eintrag löschen -dict_name.popitem() # löscht den letzten hinzugefügten Eintrag -dict_name.clear() # Löschen aller Elemente - -# gibt den entsprechenden value zurück oder false, falls er nicht existiert -dict_name.get("key") -dict_name.items() # gibt eine liste mit allen keys und values als tupel zurück -dict_name.keys() # gibt ein liste mit allen keys zurück -dict_name.values() # gibt eine liste mit allen values zurück - -###################################################################### - - -# https://www.w3schools.com/python/python_ref_dictionary.asp - diff --git a/docs/08_web/08_1_get_request.png b/docs/08_web/08_1_get_request.png new file mode 100644 index 0000000..454aac6 Binary files /dev/null and b/docs/08_web/08_1_get_request.png differ diff --git a/docs/08_web/08_1_post_request.png b/docs/08_web/08_1_post_request.png new file mode 100644 index 0000000..e4ad3f2 Binary files /dev/null and b/docs/08_web/08_1_post_request.png differ diff --git a/docs/08_web/08_1_requests.ipynb b/docs/08_web/08_1_requests.ipynb new file mode 100644 index 0000000..506238b --- /dev/null +++ b/docs/08_web/08_1_requests.ipynb @@ -0,0 +1,269 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Web-Grundlagen\n", + "\n", + "In diesem Kapitel wirst du mit der Welt da draussen kommunizieren!\n", + "\n", + "Die Grundlagen hierzu, die wir hier anschauen werden, handeln um HTTP(S)-Requests (\"Anfragen\").\n", + "\n", + "Eine HTTP(S)-Anfrage tätigst du z.B., wenn du eine Webseite mit einem Browser lädst. Rufst du z.B. die Website https://www.google.ch/ auf, dann erledigt dein Browser u.A. folgende Schritte für dich:\n", + "* Nachschauen, welcher Server sich hinter der Domaine \"www.google.ch\" befindet (DNS-Abfrage).\n", + "* Verbindungsaufbau zu diesem Server.\n", + "* HTTP-GET-Request an den Server. Folgende Daten werden von deinem Browser mitgeliefert:\n", + " * HTTP-Methode: \"GET\"\n", + " * URL: \"www.google.ch/\"\n", + " * HEADER-Daten/Meta-Daten\n", + " * wie z.B. welcher Browser verwendest du,\n", + " * bist du eingeloggt, usw.\n", + "\n", + "Ist die Anfrage erfolgreich, schickt dir der Server eine _Response_ (Antwort). Diese Response wird in diesem Falle eine Website im HTML-Format sein. Diese Antwort beschreibt, wie die Website aufgebaut ist und welcher Inhalt sie hat.\n", + "\n", + "HTML musst du nicht kennen, darfst dich aber gerne hier vertiefen: https://labs.it-ninjas.ch/docs/web/html_css/01_html_intro/" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Die Request-URL\n", + "\"Googlest\" du nach dem Begriff \"Python\", dann landest du auf dieser Seite: https://www.google.com/search?q=Python\n", + "\n", + "Schaust du diese URL genauer an, dann kannst du erkennen, dass diese URL eine bestimmte Struktur aufweist:\n", + "* `https://`: Dieser Teil beschreibt, welches Protokoll verwendet werden soll. In diesem Fall wird das verschlüsselte HTTPS-Protokoll verwendet.\n", + "* `www.google.com`: Das ist der Host/der Server. An diesen Server soll die Anfrage geschickt werden.\n", + "* `/search`: Das ist der _Path_ der Website.\n", + "* `?q=Python`: Das ist ein Parameter, der in der URL mitgegeben wurde. Als Entwickler:in könnten wir sagen: es wurde eine Variable `q` mit dem Wert `\"Python\"` übergeben. `q` steht hierbei für \"query\", also \"abfragen\"." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Eine GET-Abfrage\n", + "Schauen wir uns noch einmal das Beispiel mit der Google-Seite an, wo wir nach \"Python\" gesucht haben: https://www.google.com/search?q=Python.\n", + "\n", + "Geben wir diese URL im Browser ein, dann sendet der Browser dem entsprechenden Server automatisch eine sogenannte _GET_-Anfrage.\n", + "\n", + "Folgende Grafik zeigt vereinfacht den Flow:" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![GET Request](./08_1_get_request.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* An den Server, der hinter \"www.google.com\" steht, wird folgende HTTP-Anfrage geschickt:\n", + " * HTTP Methode: GET\n", + " * URL: https://www.google.com/search?q=Python\n", + "\n", + "* Ist die Seite erreichbar, dann erhalten wir die Website als HTML zurück.\n", + "* Zusätzlich teilt uns der _Status Code_ `200` mit, dass die Anfrage problemlos funktioniert hat." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Genau diese Anfrage können wir auch mit Python ausführen.\n", + "\n", + "Um HTTP-Requests durchzuführen, benötigst du eine Dependency wie `requests`. Installiere diese:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! echo requests > ./requirements.txt\n", + "! pip install -r ./requirements.txt" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nun kannst du die HTTP-GET-Anfrage wie folgt ausführen:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "\n", + "response = requests.get(\"https://www.google.com/search?q=Python\")\n", + "\n", + "response.status_code, response.text" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mit einem HTML kannst du vielleicht noch nicht viel anfangen. Stattdessen könnte eine Anfrage auch ein _JSON_ zurückgeben.\n", + "\n", + "Folgende Anfrage gibt ein JSON zurück, welches wir dann auch gleich laden:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "import requests\n", + "\n", + "response = requests.get(\n", + " \"https://raw.githubusercontent.com/it-ninjas/labs/master/static/files/json/islands.json\")\n", + "\n", + "# Wandle die Antwort in ein Dictionary um:\n", + "islands = response.json()\n", + "\n", + "# Etwas mit der Antwort anfangen:\n", + "print(\"Here are some Islands:\")\n", + "print([island[\"name\"] for island in islands])\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## POST-Request\n", + "Beim GET-Request hast du bereits die Grundlagen von HTTP-Anfragen angeschaut. Du hast gesehen, dass du spezifische Daten in der URL angeben konntest.\n", + "\n", + "In vielen Situationen möchtest es vermeiden, die Daten in der URL mitgeben zu müssen. Hierfür können die Daten von der URL (nach dem \"?\") in den sogenannten _Request-Body_ verschoben werden.\n", + "\n", + "Bei der `requests.post(...)`-Funktion kannst du die Daten direkt beim `data`-Argument mitgegen. Sind deine Daten als Dictionary vorhanden, dann kannst du diese stattdessen als `json`-Argument übergeben. Einer dieser Werte wird dann in den Request-Body gepackt.\n", + "\n", + "Im nachfolgenden Beispiel senden wir einen Request an die JSONPlaceholder-API. Das Ziel ist es, einen neuen \"Post\" zu erstellen. Das mitgelieferte Dictionary repräsentiert einen solchen Post:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = requests.post(\"https://jsonplaceholder.typicode.com/posts\",\n", + " json={\n", + " \"userId\": 10,\n", + " \"title\": \"Post from Python\",\n", + " \"body\": \"Did id work?\"})\n", + "\n", + "response.status_code, response.text" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die POST-Anfrage hat wie folgt ausgesehen:\n", + "\n", + "![POST Request](./08_1_post_request.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Im letzten Beispiel mussten wir die HTTP-POST-Methode verwenden und durften nicht GET verwenden. Dies hatte diese beiden Gründe:\n", + "1. `GET` lässt keinen HTTP-Body zu (bzw. ist nicht der Sinn von GET). `POST` hingegen schon.\n", + "2. Die JSONPlaceholder-API erwartet für diesen Zweck die `POST`-Methode." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Weitere HTTP-Methoden\n", + "GET- und POST sind die bekanntesten HTTP-Methoden. Du kennst nun den wichtigsten Unterschied von beiden Methoden: wo werden die Daten mitgegeben.\n", + "\n", + "Die Information, ob es sich um einen GET- oder POST-Request handelt, wird der Server sehen können. Als Schlussfolgerung kann der Server einen anderen Code ausführen je nach Methode.\n", + "\n", + "Neben GET und POST gibt es noch viele weitere. Die Methoden werden meistens so gewählt, dass sie mit dem Zweck der Anfrage übereinstimmen:\n", + "\n", + "| Methode | Zweck |\n", + "| --------- | ------------------------------------------------------------------------- |\n", + "| GET | Daten abfragen. |\n", + "| POST | Daten senden, um etwas Neues zu erstellen. |\n", + "| PUT | Daten ersetzen bzw. erstellen wenn noch nicht präsent. |\n", + "| PATCH | Ähnlich wie PUT, aktualisiert aber nur spezifizierte Werte. |\n", + "| DELETE | Löscht bestimmte Daten. |\n", + "| OPTIONS | Abfrage, welche HTTP-Methoden für einen bestimmten Pfad verfügbar sind. |\n", + "\n", + "Eine bessere Übersicht erhältst du hier: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## HTTP-Status-Code\n", + "\n", + "Wenn eine Anfrage erfolgreich war, dann erhältst du meistens den Status-Code `200` (\"OK\") oder `201` (\"Created\"). Kennen tust du bestimmt auch den Status-Code `404` \"Not Found\".\n", + "\n", + "Solche Status Codes geben darüber Auskunft, ob ein Request funktioniert hat bzw. was der Status des Requests ist.\n", + "\n", + "Eine lustige Seite, auf welcher du die einzelnen Status-Codes nachschauen gehen kannst, ist z.B. https://http.cat/. Eine seriösere Seite ist https://developer.mozilla.org/en-US/docs/Web/HTTP/Status.\n", + "\n", + "Zusammengefasst bedeuten diese Status-Codes kategorisch folgendes:\n", + "\n", + "| Code | Type | Beispiel |\n", + "| ----- | --------------------------- | --------------------------------------------------------------- |\n", + "| 2XX | Success (erfolgreich) | Anfrage war erfolgreich, Daten wurden gespeichert. |\n", + "| 3XX | Redirection (Weiterleitung) | User muss sich zuerst einloggen. |\n", + "| 4XX | Client Error (Fehler desjenigen, der anfragt) | Fehlerhafter Request, Path nicht verfügbar. |\n", + "| 5XX | Server Error | Auf dem Server ist ein Fehler aufgetreten. |" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.3" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/08_web/08_2_hello_world_browser.png b/docs/08_web/08_2_hello_world_browser.png new file mode 100644 index 0000000..1b7b927 Binary files /dev/null and b/docs/08_web/08_2_hello_world_browser.png differ diff --git a/docs/08_web/08_2_web_server.ipynb b/docs/08_web/08_2_web_server.ipynb new file mode 100644 index 0000000..cd59061 --- /dev/null +++ b/docs/08_web/08_2_web_server.ipynb @@ -0,0 +1,190 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Web-Server\n", + "\n", + "Vorher haben wir uns angeschaut, wie wir in Python eine HTTP-Anfrage auf einen anderen Server schicken können.\n", + "\n", + "Nun kehren wir den Spiess um:\n", + "\n", + "Wir stellen den Web-Server zur Verfügung und akzeptieren eingehende HTTP-Anfragen.\n", + "\n", + "Hierfür bauen wir uns eine sehr kleine Webanwendung mit dem leichtgewichtigen Web-Framework \"Flask\". Das Tolle an Flask ist, dass es sehr minimalistisch ist und trotzdem sehr viele wichtige Features für ein Backend von Haus aus anbietet.\n", + "\n", + "Für den Moment kannst du Flask via Kommandozeile so installieren:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! pip install flask" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nun kannst du Flask bereits benutzen.\n", + "\n", + "Im Python-Code kannst du dann eine Flask-App erstellen, indem du den Konstruktor der Klasse `Flask` aufrufst. Den Rückgabewert - also das `Flask`-Objekt - benötigst du nachher noch, deswegen speicherst du ihn in einer Variable wie `app`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from flask import Flask\n", + "\n", + "app = Flask(__name__)\n", + "\n", + "\n", + "@app.route('/')\n", + "def hello():\n", + " return 'Hello, World!'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dann haben wir eine Funktion mit `@app.route('/')` annotiert. Dies hat den Zweck, dass die annotierte Funktion aufgerufen wird, wenn der Pfad \"[unser Server]/\" im Browser geladen wird.\n", + "\n", + "Das Ganze kannst du ausprobieren, indem du nächsten Code ausführen lässt:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if __name__ == '__main__':\n", + " app.run()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nun kannst du folgende URL im Browser des gleichen Computers laden: http://127.0.0.1:5000/\n", + "\n", + "Du solltest nun im Browser den Text \"Hello, World!\" sehen:\n", + "\n", + "![Hello, World!](./08_2_hello_world_browser.png)\n", + "\n", + "Dieser Text ist genau der Output der Funktion `hello()`.\n", + "\n", + "Wenn du dann weiterentwickelst, vergiss nicht, diese Ausführung wieder zu beenden, damit der Server gestoppt wird!" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Standardmässig geben solche Methoden, die mit `@app.route(...)` annotiert sind, eine Webseite - also ein HTML-String - zurück.\n", + "\n", + "Nachfolgend das gleiche Beispiel mit einer \"richtigen\" HTML-Seite:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@app.route('/home')\n", + "def about():\n", + " return '''\n", + "

Hello

\n", + "

Welcome to my Website!

\n", + "

You can only expect the best on this page!

\n", + "
\n", + " \n", + " \n", + "
\n", + "
\n", + "