Commit cbef1e07 authored by christian.foerster's avatar christian.foerster

update

parent 0040f636
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,12 +6,39 @@
"source": [
"# A short Introdcution to Python\n",
"\n",
"## Things you might be wondering:\n",
"- What is an Anaconda and what does it have to do with Python\n",
" - Anaconda program to manage virtual environments\n",
" - can be set up in a virtual environment\n",
" \n",
" \n",
"- Python 2 vs Python 3?\n",
" - unsupported vs. supported\n",
" \n",
" \n",
"- Where does Python fit in? \n",
" - for educational purposes (https://www.researchgate.net/publication/328175547_MatLab_vs_Python_vs_R)\n",
" - more generally speaking (https://pyzo.org/python_vs_matlab.html) (R is out mainly for data wrangling)\n",
" - development -> if you want to develop open source software, that many people can use, you need a open source language!\n",
" \n",
" \n",
"- Is Python fast?\n",
" - not really\n",
" - it can comparable to c++/c/fortran (numpy, cython, numba)\n",
" \n",
" \n",
"- Why is Python so popular?\n",
" - easy to learn/use\n",
" - code readabiltiy \n",
" - open source \n",
" - huge commuinuty\n",
" - general purpose language\n",
"\n",
"## Some nice-to-know's\n",
"* indexing starts at 0\n",
"* the syntax i controlled mainly by tabs (4 spaces), that mean less brackets and better readability\n",
" * so you should adapt your texteditor (e.g.: notepad++) to replace a tab with 4 spaces\n",
"* commet with hashtag, multiline comments/docstrings like so \"\"\"my comment or docstring\"\"\""
"* commet with hashtag, multiline comments/docstrings like so \"\"\"my comment or docstring\"\"\"\n"
]
},
{
......@@ -23,7 +50,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 3,
"metadata": {},
"outputs": [
{
......@@ -43,7 +70,7 @@
"25"
]
},
"execution_count": 1,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
......@@ -881,6 +908,21 @@
"[(True,i) if i%2==0 else (False,i) for i in range(29)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Learning by doing!\n",
"### Task 1\n",
"\n",
"In order to not fall asleep when looking at the python syntax, we'll try to do so with a little task in mind!\n",
"Lets write a little program which can be used to analyse files and can count the occurance of patterns in each line!\n",
"\n",
"__Pattern Analyser__\n",
" - input: A string! (later a file)\n",
" - output: Number of occurances in each line!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
......@@ -953,7 +995,7 @@
},
{
"cell_type": "code",
"execution_count": 71,
"execution_count": 2,
"metadata": {},
"outputs": [
{
......@@ -962,14 +1004,15 @@
"3000"
]
},
"execution_count": 71,
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import sys\n",
"sys.getrecursionlimit()"
"sys.getrecursionlimit()\n",
"#sys.setrecursionlimit()"
]
},
{
......@@ -1022,7 +1065,7 @@
"%timeit factorial_fast(2000)\n",
"%timeit factorial_fastest(2000)\n",
"\n",
"# for is faster than while"
"# for is faster than while\n"
]
},
{
......@@ -3047,13 +3090,98 @@
"## Object Oriented Programming"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4 Pillars of OOP\n",
"- Inheritance (a way to reuse your code)\n",
"- Abstraction (showing only essential features hiding details)\n",
"- Encapsulation (bind data variables and functions together in a class)\n",
"- Polymorphism (create functions with same name and different arguments, redefine functions)"
]
},
{
"cell_type": "code",
"execution_count": 132,
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# Parent class\n",
"class Dog:\n",
" # Class attribute\n",
" species = 'mammal'\n",
"\n",
" # Initializer / Instance attributes\n",
" def __init__(self, name, age):\n",
" self.name = name\n",
" self.age = age\n",
"\n",
" # instance method\n",
" def description(self):\n",
" return \"{} is {} years old\".format(self.name, self.age)\n",
"\n",
" # instance method\n",
" def speak(self, sound):\n",
" return \"{} says {}\".format(self.name, sound)\n",
"\n",
" \n",
"# Child class (inherits from Dog() class)\n",
"class RussellTerrier(Dog):\n",
" def run(self, speed):\n",
" return \"{} runs {}\".format(self.name, speed)\n",
"\n",
"\n",
"# Child class (inherits from Dog() class)\n",
"class Bulldog(Dog):\n",
" def run(self, speed):\n",
" return \"{} runs {}\".format(self.name, speed)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Child classes inherit attributes and\n",
"# behaviors from the parent class\n",
"jim = Bulldog(\"Jim\", 12)\n",
"print(jim.description())\n",
"\n",
"# Child classes have specific attributes\n",
"# and behaviors as well\n",
"print(jim.run(\"slowly\"))\n",
"\n",
"# Is jim an instance of Dog()?\n",
"print(isinstance(jim, Dog))\n",
"\n",
"# Is julie an instance of Dog()?\n",
"julie = Dog(\"Julie\", 100)\n",
"print(isinstance(julie, Dog))\n",
"\n",
"# Is johnny walker an instance of Bulldog()\n",
"johnnywalker = RussellTerrier(\"Johnny Walker\", 4)\n",
"print(isinstance(johnnywalker, Bulldog))\n",
"\n",
"# Is julie and instance of jim?\n",
"print(isinstance(julie, RussellTerrier))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Special Methods, hidden Variables, ...\n",
"Fancy things we can do!"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# thats how you define a class\n",
"class Bank_Account():\n",
" bank = \"Some Bank\"\n",
" '''my documentation'''\n",
......@@ -3095,7 +3223,7 @@
},
{
"cell_type": "code",
"execution_count": 133,
"execution_count": 3,
"metadata": {},
"outputs": [
{
......@@ -3111,7 +3239,7 @@
"667"
]
},
"execution_count": 133,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
......@@ -3126,30 +3254,6 @@
"Jana+Steffen"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 134,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"This is an instance of the class Bank_Account.\n"
]
}
],
"source": [
"repr()"
]
},
{
"cell_type": "markdown",
"metadata": {},
......@@ -3233,6 +3337,67 @@
"So jetzt zeigen ich einfach mal ein paar Beispiele und die kannst du ja mal nachvollziehen. Danach bekommst du ein paar Übungsaufgaben."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Notepad():\n",
" def __init__(self):\n",
" self.notes = {}\n",
" self.selectedNote=None\n",
" return\n",
" \n",
" def _noteNameCheck(self,noteName):\n",
" if noteName is None and self.selectedNote is None:\n",
" raise ValueError(\"Please provide a noteName.\")\n",
" elif noteName is None:\n",
" noteName = self.selectedNote\n",
" return noteName\n",
" \n",
" def addNote(self, noteName):\n",
" self.selectedNote=noteName\n",
" self.notes[noteName] = {}\n",
" return self.notes\n",
" \n",
" def deleteNote(self, noteName=None):\n",
" noteName = self._noteNameCheck(noteName)\n",
" del self.notes[noteName]\n",
" self.selectedNote = None \n",
" return self.notes\n",
" \n",
" def addEntry(self, entry, noteName=None):\n",
" noteName = self._noteNameCheck(noteName)\n",
" self.notes[noteName][entry]=\"unmarked\"\n",
" return self.notes\n",
" \n",
" def deleteEntry(self, entry, noteName=None):\n",
" noteName = self._noteNameCheck(noteName)\n",
" del self.notes[noteName][entry]\n",
" return self.notes\n",
" \n",
" def markEntry(self, entry, noteName=None):\n",
" noteName = self._noteNameCheck(noteName)\n",
" self.notes[noteName][entry]=\"marked\"\n",
" return self.notes\n",
" \n",
"NotepadInstance = Notepad()\n",
"NotepadInstance.addNote(\"Shopping\")\n",
"NotepadInstance.addEntry(\"coffee\")\n",
"NotepadInstance.addEntry(\"milk\")\n",
"NotepadInstance.addEntry(\"break\")\n",
"NotepadInstance.addEntry(\"chocolate\")\n",
"NotepadInstance.markEntry(\"chocolate\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 101,
......
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Large Projects\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Naming Convetions and Best Practices\n",
"\n",
"How does one name their classes, methods, variables, files, modules, packages,...? \n",
"How should I structure my code, how should I code,...?\n",
"\n",
"https://visualgit.readthedocs.io/en/latest/pages/naming_convention.html \n",
"https://gist.github.com/sloria/7001839 \n",
"https://docs.python-guide.org/writing/style/ \n",
"https://martin-thoma.com/python-style-guide/\n",
"\n",
"Use **Git** to track changes in your code, create versions, ... \n",
"Make your code more accessable to others to use or to help you working on it via using a Git **hosting service** like **GitLab** (https://gitlab.switch.ch/)!\n",
"\n",
"A good **IDE** for larger projects is **PyCharm** by jetbrains (https://www.jetbrains.com/pycharm/). \n",
"They have a free community version! \n",
"\n",
"Why is PyCharm good? (https://realpython.com/pycharm-guide/)\n",
" - helps you with virtual environment management (a new python env. for each project)\n",
" - tracking your files via git made easier\n",
" - very good for debugging\n",
" - it's the smartes IDE for python (smart -> helps to remind you of good practices, coding tipps, autocompletion, ...)\n",
" - ...\n",
"\n",
"**Most importantly: Do not be affraid of the command line!**\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tank Example\n",
"\n",
"The goal here is to show you how to aget from an idea (a bit of quick and dirty code) to a fully setup and documented python package. We'll be stepping through this entire process not necessarily in the right order! But for demonstration purposes this way is better!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Version 1\n",
"\n",
"Just trying out our code and see whether everything works"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"89.74001978772792\n"
]
}
],
"source": [
"# 4 tanks: 1 -> 4, 2 -> 3, 3 -> 4\n",
"\n",
"# the tanks\n",
"tank_1 = dict(name=\"tank_1\", level=50, rate=0.21)\n",
"tank_2 = {\"name\":\"tank_2\", \"level\":80, \"rate\":tank_1[\"rate\"]/2}\n",
"tank_3 = dict(name=\"tank_3\", level=5, rate=0.09)\n",
"tank_4 = dict(name=\"tank_4\",level=0, rate=0.01) # tank has a leak\n",
"\n",
"# run the model\n",
"for i in range(20):\n",
" Q_4 = tank_4[\"level\"]*tank_4[\"rate\"]\n",
" tank_4[\"level\"] -= Q_4\n",
" \n",
" Q_34 = tank_3[\"level\"]*tank_3[\"rate\"]\n",
" tank_3[\"level\"] -= Q_34\n",
" tank_4[\"level\"] += Q_34\n",
" \n",
" Q_14 = tank_1[\"level\"]*tank_1[\"rate\"]\n",
" tank_1[\"level\"] -= Q_14\n",
" tank_4[\"level\"] += Q_14\n",
" \n",
" Q_23 = tank_2[\"level\"]*tank_2[\"rate\"]\n",
" tank_2[\"level\"] -= Q_23\n",
" tank_3[\"level\"] += Q_23\n",
" \n",
"# check the model\n",
"print(tank_4[\"level\"])\n",
"\n",
"\n",
"\n",
"\n",
"# Thoughts:\n",
"# What about if the setup changes and we want more tanks?\n",
"# Will other people understand my code?\n",
"# Could other people use this code, write their own model?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Version 2\n",
"\n",
"Lets try to change our code so it represents the reality better and is more generic. \n",
"It would be great to have a better representation for a tank, \n",
"so that someone else using our program\n",
"also understands whats goin on."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# creating a tank class and defining its representation\n",
"class Tank:\n",
" # run when initating an instance\n",
" def __init__(self, tank_id, level, rate):\n",
" self.tank_id = tank_id\n",
" self.level = level\n",
" self.rate = rate\n",
" \n",
" # our joice of representation for a tank!\n",
" # so that you and the user knows whats happening\n",
" # accessable with print(tank_instance) or repr(tank_instance)\n",
" def __repr__(self):\n",
" return \"<type Tank>\\ntank_id: {}\\nlevel: {}\\nrate: {}\".format(self.tank_id,self.level,self.rate)\n",
" \n",
"# the tanks\n",
"tank_1 = Tank(\"tank_1\", 50, 0.21)\n",
"tank_2 = Tank(\"tank_2\", 80, tank_1.rate/2)\n",
"tank_3 = Tank(\"tank_3\", 5, 0.09)\n",
"tank_4 = Tank(\"tank_4\", 0, 0.01) # tank has a leak\n",
"\n",
"# run the model\n",
"for i in range(20):\n",
" Q_4 = tank_4.level*tank_4.rate\n",
" tank_4.level -= Q_4\n",
" \n",
" Q_34 = tank_3.level*tank_3.rate\n",
" tank_3.level -= Q_34\n",
" tank_4.level += Q_34\n",
" \n",
" Q_14 = tank_1.level*tank_1.rate\n",
" tank_1.level -= Q_14\n",
" tank_4.level += Q_14\n",
" \n",
" Q_23 = tank_2.level*tank_2.rate\n",
" tank_2.level -= Q_23\n",
" tank_3.level += Q_23\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Version 3\n",
"\n",
"Discharge is a property of a tank. \n",
"Or in other words each tank has its own discharge so let's code it like that."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"# adding discharge as property of a tank so we should add this to our Tank class\n",
"class Tank:\n",
" # run when initating an instance\n",
" def __init__(self, tank_id, level, rate):\n",
" self.tank_id = tank_id\n",
" self.level = level\n",
" self.rate = rate\n",
" \n",
" # the method Q calculates the flow of a given tank every time it's called\n",
" @property # allows use of the method without brackets \n",
" def Q(self):\n",
" return self.level * self.rate\n",
" \n",
" # our joice of representation for a tank!\n",
" # so that the user knows whats happening\n",
" def __repr__(self):\n",
" return \"<type Tank>\\ntank_id: {}\\nlevel: {}\\nrate: {}\".format(self.tank_id,self.level,self.rate)\n",
"\n",
"# the tanks\n",
"tank_1 = Tank(\"tank_1\", 50, 0.21)\n",
"tank_2 = Tank(\"tank_2\", 80, tank_1.rate/2)\n",
"tank_3 = Tank(\"tank_3\", 5, 0.09)\n",
"tank_4 = Tank(\"tank_4\", 0, 0.01) # tank has a leak\n",
"\n",
"# run the model\n",
"for i in range(20):\n",
" Q_4 = tank_4.Q\n",
" tank_4.level -= Q_4\n",
" \n",
" Q_34 = tank_3.Q\n",
" tank_3.level -= Q_34\n",
" tank_4.level += Q_34\n",
" \n",
" Q_14 = tank_1.Q\n",
" tank_1.level -= Q_14\n",
" tank_4.level += Q_14\n",
" \n",
" Q_23 = tank_2.Q\n",
" tank_2.level -= Q_23\n",
" tank_3.level += Q_23\n",
" \n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Version 4\n",
"\n",
"Filling the last tank still requires lots of code and we would have to adapt this part of the code for different configurations. \n",
"This could be very laborsome if it's a large network. How can we improve in that?"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"# adding a tanks capability to fill up via knowing its upstream tank!\n",
"class Tank:\n",
" def __init__(self, tank_id, level, rate, upstream_tanks=None):\n",
" self.tank_id = tank_id\n",
" self.level = level\n",
" self.rate = rate\n",
" self.upstream_tanks = upstream_tanks\n",
" \n",
" # the method Q calculates the flow of a given tank every time it's called\n",
" @property # allows use of the method without brackets \n",
" def Q(self):\n",
" return self.level * self.rate\n",
" \n",
" # filling a tank from all its upstream tanks\n",
" def fill_tank(self):\n",
" self.level -= self.Q # reducing level of current tank\n",
" if self.upstream_tanks is not None: # if the current tank has upstream tanks\n",
" for uptank in self.upstream_tanks: # do for each upstream tank\n",
" self.level += uptank.Q # add discharge of upstream tank to current tank\n",
" # recursion\n",
" uptank.fill_tank() # filling up upstream tank\n",
" \n",
" # our joice of representation for a tank!\n",
" # so that the user knows whats happening\n",
" def __repr__(self):\n",
" return \"<type Tank>\\ntank_id: {}\\nlevel: {}\\nrate: {}\".format(self.tank_id,self.level,self.rate)\n",
"\n",
" \n",
"# the tanks\n",
"tank_1 = Tank(\"tank_1\", 50, 0.21)\n",
"tank_2 = Tank(\"tank_2\", 80, tank_1.rate/2)\n",
"tank_3 = Tank(\"tank_3\", 5, 0.09, upstream_tanks=[tank_2])\n",
"tank_4 = Tank(\"tank_4\", 0, 0.01, upstream_tanks=[tank_1, tank_3]) # tank has a leak\n",
"\n",
"# run the model\n",
"for i in range(20):\n",
" tank_4.fill_tank()\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Version 5\n",
"\n",
"Adding two more convenient functions. \n",
"- Wouldn't it be nice to automatically generate the tanks? Imagine reading the data from a GIS layer and using it from there...\n",
"- What about the levels? We want to know what is happending to the tank levels so lets create a function to retrieve this info..."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [],
"source": [
"# A tank is a object (-> class) we can touch,\n",
"# it has attributes like level and id and so on,\n",
"# discharge is property of a tank, furthermore it does have the capability to fill up (a function or method)\n",
"class Tank:\n",
" def __init__(self, tank_id, level, rate, upstream_tanks=None):\n",
" self.tank_id = tank_id\n",
" self.level = level\n",
" self.rate = rate\n",
" self.upstream_tanks = upstream_tanks\n",
" \n",
" # the method Q calculates the flow of a given tank every time it's called\n",
" @property # allows use of the method without brackets \n",
" def Q(self):\n",
" return self.level * self.rate\n",
" \n",
" # filling a tank from all its upstream tanks\n",
" def fill_tank(self):\n",
" self.level -= self.Q\n",
" if self.upstream_tanks is not None:\n",
" for uptank in self.upstream_tanks:\n",
" # recursion (sort of)\n",
" self.level += uptank.Q\n",
" uptank.fill_tank()\n",
" \n",
" # our joice of representation for a tank!\n",
" # so that the user knows whats happening\n",
" def __repr__(self):\n",
" return \"<type Tank>\\ntank_id: {}\\nlevel: {}\\nrate: {}\".format(self.tank_id,self.level,self.rate)\n",
"\n",
"\n",
"# in order to automatically init our tanks we will have to \n",
"# provide the network structure and attributes of all tanks.\n",
"# a graph would be a good way to go. But we want to keep thinks nice and simple!\n",
"# lets go with some dictionaries, we could write adapters for graphs later...\n",
"# network_structure = {tank_down_i:[tank_up_i,tank_up_(i+1),...],} eg.: {6:[4,5],4:[1,2],5:[3]}\n",
"# attributes = {tank_id:(tank_level, tank_rate),...}\n",
"def initiate_tanks(network_structure, attributes):\n",
" # init all tanks without upstream tanks\n",
" tanks = {}\n",
" for tank_id in attributes:\n",
" level = attributes[tank_id][0]\n",
" rate = attributes[tank_id][1]\n",
" tanks[tank_id] = Tank(tank_id, level, rate, upstream_tanks=None)\n",
" # add upstream tanks\n",
" for tank_id in network_structure:\n",
" tanks[tank_id].upstream_tanks = [tanks[tank_id_up] for tank_id_up in network_structure[tank_id]]\n",
" return tanks\n",
"\n",
"# iterate over all tanks and retrieve these levels with id\n",
"def get_levels(tanks):\n",
" fill_dict={}\n",
" for tank_id, tank in tanks.items():\n",
" fill_dict[tank_id] = tank.level \n",
" return fill_dict\n",
"\n",
"\n",
"### --- User Code ---\n",
"\n",
"## define network and attributes \n",
"network_structure = {\"tank_6\":[\"tank_4\",\"tank_5\"],\"tank_4\":[\"tank_1\",\"tank_2\"],\"tank_5\":[\"tank_3\"]}\n",
"attributes = {\"tank_1\":(10, 0.0),\n",
" \"tank_2\":(20, 0.06),\n",
" \"tank_3\":(15, 0.9),\n",
" \"tank_4\":(8, 0.3),\n",
" \"tank_5\":(44, 0.5),\n",
" \"tank_6\":(2.5, 0.0)}\n",
"\n",
"## generate the network\n",
"all_tanks = initiate_tanks(network_structure,attributes)\n",