From 23f03e90d1d8a9311f7a1b7389d297930eaa408e Mon Sep 17 00:00:00 2001 From: Philip Date: Tue, 15 Jul 2025 17:04:58 +0200 Subject: [PATCH] day1-code --- src/T00_Intro.py | 66 ++++++++++++++++ src/T01_Funktionen.py | 138 +++++++++++++++++++++++++++++++++ src/T02_Datentypen.py | 72 +++++++++++++++++ src/T03_Klassen.py | 81 +++++++++++++++++++ src/T04_Klassen_privat.py | 99 +++++++++++++++++++++++ src/T05_Klassen_properties.py | 52 +++++++++++++ src/T06_Klassen_dataclasses.py | 46 +++++++++++ src/T07_pandas.py | 113 +++++++++++++++++++++++++++ src/T08_WorldCities.py | 25 ++++++ 9 files changed, 692 insertions(+) create mode 100644 src/T00_Intro.py create mode 100644 src/T01_Funktionen.py create mode 100644 src/T02_Datentypen.py create mode 100644 src/T03_Klassen.py create mode 100644 src/T04_Klassen_privat.py create mode 100644 src/T05_Klassen_properties.py create mode 100644 src/T06_Klassen_dataclasses.py create mode 100644 src/T07_pandas.py create mode 100644 src/T08_WorldCities.py diff --git a/src/T00_Intro.py b/src/T00_Intro.py new file mode 100644 index 0000000..be9002c --- /dev/null +++ b/src/T00_Intro.py @@ -0,0 +1,66 @@ +# git clone https://git.budem.de/philip/tennet_course.git + +# Standardpython + +# externe libraries +# pandas, geopandas, matplotlib, numpy, etc + +a = 13 +print(a, type(a)) +s = "hello" +t = 'python' +print(s, t) + +# unveränderlichen datentypen und veränderlichen Datentypen +# immutable und mutable + +print(id(a)) +b = a # erstellt immer eine NamensReferenz auf das Objekt auf der rechten seite +print(id(b)) +print() +a += 1 # a = a + 1 +print(id(a)) +print(a) +print(id(b)) +print(b) + +# unveränderliche Objekte: +# * Zahlen: int, float, (complex) +# * Strings +# * tuple (unveränderliche Liste) (nur wenn unveränderliche elemente im tuple sind) +# * (frozenset) +# alles andere ist veränderlich + +s = "Hello World" +s = s.upper() +print(s) +s = s.replace("WORLD", "PYTHON") +print(s) + +# Listen sind veränderlich + +l1 = [5, "python", 1.3] +l2 = l1 # erzeugt einen zweiten verweis auf die Liste [5, "python", 1.3] +l1.append(42) +print(l2) +# += besser append und extend nutzen +l1 += [6, 1, 9] +print(l1) +print(l2) +# + (erzeuge eine neue Liste) +print(id(l1)) +l1 = l1 + [1] +print(id(l1)) +print(l1) +print(l2) +# best practise: append und extend +l1.append(15) + +l1 = [1, 2, 3] +l2 = [4, 5, 6] +l1.extend([5, 8]) +print(l1) +l1.extend("python") # jedes element einzeln eingefügt +print(l1) +l1.extend(["python"]) +print(l1) diff --git a/src/T01_Funktionen.py b/src/T01_Funktionen.py new file mode 100644 index 0000000..2159ebc --- /dev/null +++ b/src/T01_Funktionen.py @@ -0,0 +1,138 @@ +# float: float oder integer +DEFAULT_B = 13 # all_caps + +SPEED_OF_SOUND = 343 + + +def divide(a, b): + # nicht überprüfen ob b != 0 da dies ein fehler ist! + return a / b + +# : typehint +def convert_dog_to_human_age(dogage: int) -> int: + """ + Konvertiert ein Hundealter in Menschenjahren. + :param dogage: (int) Hundealter in Jahren + :return (int): Entsprechendes alter in Menschenjahren + Ein Hund ist immer ganze Jahre alt. z.B. 1, 2, 3, 4 + """ + # assert oder raise + assert dogage >= 0, "ALter muss immer positiv sein" + # assert isinstance(dogage, int), "Muss integer sein" + human_age = 0 # lokale variable + if dogage == 1: + human_age = 14 + elif dogage == 2: + human_age = 22 + elif dogage > 2: + human_age = 22 + (dogage - 2) * 5 + + return human_age + +def substract_mean(lst: list[float]) -> None: + mean = sum(lst) / len(lst) + for i in range(len(lst)): + lst[i] -= mean + # diese Zeile steht immer bei fehlendem return am Ende + return None + +def create_mean_free_list(lst): + res = [] + mean = sum(lst) / len(lst) + for el in lst: + res.append(el - mean) + return res +# positionelle argumente, schlüsselwort-argumente +def foo(a, b=DEFAULT_B, verbose=0): + # f-string + # formatted-string + print(f"a={a}") + print(f"{b=}") + print(f"{verbose=}") + print(f"a/b = {a/b}") + # r-string + return a / b + +def open_file(file_path: str): + print(f"Opening {file_path}") + +def food(bag=None, debug=False): + # is und nicht == + # is vergleicht id + if debug: + print("Debug message") + if bag is None: + bag = [] + bag.append("Spam") + return bag + +def calculate_time(dist): + # 1. in der Funktion nach SPEED_OF_SOUND + # 2. Global im file + imports (selten nutzen) + # 3. im eingebauten namensraum + return dist / SPEED_OF_SOUND + + +def calc_circle_area(radius): + return 3.14 * radius ** 2 + +def calc_rect_area(height, width): + return height * width + +def calc_area(shape, *sides: tuple[float], **kwargs): + print(kwargs, type(kwargs)) + if "verbose" in kwargs: + verbose = kwargs.pop("verbose") # entfernt schlüssel verbose und gibt den dazugehörigen wert zurück + print(verbose) + if shape.lower() == "circle": + return calc_circle_area(*sides, **kwargs) + elif shape.lower() == "rectangle": + return calc_rect_area(*sides, **kwargs) # args[0], args[1] + else: + raise ValueError(f"Shape {shape} is not yet supported, Circle, Rectangle") + +def complete_function(pos_arg1, pos_argn, *args, kw1=True, kwn="N", **kwargs): + print(pos_arg1) + +if __name__ == "__main__": + dst = 10 + print("test") + t = "hello" + # Beispiele + print(__name__) + scooby_doo = 6 + scooby_name = "Scooby doo" + scooby_doo_human = convert_dog_to_human_age(scooby_doo) + print(scooby_doo_human) + divide(5, 1) + numbers = [42, 14, 7, 11, 13, 39] + print(numbers, id(numbers)) + res = numbers.copy() + substract_mean(res) + print(numbers) + numbers.insert(0, -1) + print(numbers) + foo(31, verbose=2) + foo(10, b=3) + foo(b=5, a=2) # nicht schreiben + datei = r"C:\neuer Ordner\neuesfile" # raw-string + open_file(datei) + res = food() + print(res) + res2 = food() + print(res2) + res3 = food() + print(res3) + print(res) + l1 = [101, 102, 103] + l2 = [101, 102, 103] + print(l1 == l2) + t = calculate_time(10) + print(t) + area = calc_area("rectangle", 5, 2) + print(area) + area = calc_area("circle", 4, verbose=True) + print(area) + rect = [6, 2] + ra = calc_rect_area(*rect) + print(ra) \ No newline at end of file diff --git a/src/T02_Datentypen.py b/src/T02_Datentypen.py new file mode 100644 index 0000000..fb985c2 --- /dev/null +++ b/src/T02_Datentypen.py @@ -0,0 +1,72 @@ +from copy import deepcopy +# Listen +l = ["python", 42,18, 4, 2, 19] +print(l[0]) +print(l[2:4]) # inklusive:exklusive +print(len(l)) +for i in range(len(l)): + print(i, l[i]) +l2 = [[1, 2, 3], [10, 11, 12]] +print(l2) +print(l2[0]) +print(l2[0][1]) +# kopien von veschachtelten strukturen +l_copy = l2.copy() +l_copy[0][0] = 10 +print(l2) +# was ist eine Liste +# sammlung an verweise + + +l2 = [[1, 2, 3], [10, 11, 12]] +print(l2) +print(l2[0]) +print(l2[0][1]) +# kopien von veschachtelten strukturen +l_copy = deepcopy(l2) +l_copy[0][0] = 10 +print(l2) +# python ist objektorientiert aufgebaut + +# Dictionaries (ungeordnete Datenstruktur) +prices = {"bananas": 2.19, "oranges": 3.39, "ice-cream": 2.79, 0:"zero"} +print(prices["oranges"]) +print(len(prices)) +print(prices) +print(prices[0]) +# hinzufügen von schlüsselwertpaaren +prices["ice-tea"] = 0.99 +print(prices) +prices["bananas"] = 2.49 # update den wert +print(prices["bananas"]) +print(prices) +prices.pop(0) +print(prices) +# über ein dictionary iterieren +for product in prices: # über die schlüssel + print(f"{product:10s} {prices[product]:7.2f}") + +print() +for product, price in prices.items(): + print(f"{product:10s} {price:7.2f}") + reduced_price = price * 0.9 + +product_names = ["bananas", "oranges", "ice-cream", "ice_tea"] +# gibt es ein element in einer Liste bzw. einen Schlüssel in einem dictionary +# in bei listen ist eine for-schleife (lineare Laufzeit) +if "coffee" in product_names: + print("Coffee available") +else: + print("Coffee not available") + +# in bei dict: konstante Zeit +if "coffee" in prices: # überprüft nur schlüssel + print("Coffee available") +else: + print("Coffee not available") + +# oder set (merkt sich nicht die reihenfolge des einfügens) + +# schlüssel im dictionary: +# 1. unveränderlich +# 2. hashable \ No newline at end of file diff --git a/src/T03_Klassen.py b/src/T03_Klassen.py new file mode 100644 index 0000000..2910db2 --- /dev/null +++ b/src/T03_Klassen.py @@ -0,0 +1,81 @@ + +# PascalCase +class Robot: + # alle funktionen mit unterstrichen links und rechts müssen exakt so heißen + def __init__(self, name): + print(f"Creating a new robot named {name}") + # sollten immer alle attribute initialisiert werden + self.name = name + + def say_hi(self): + print(f"Hello, I'm a robot called {self.name}") + +r1 = Robot("Bender") # führt die init-funktion aus +print(r1.name) +r1.say_hi() # r1 -> Robot -> Robot.say_hi(r1) +Robot.say_hi(r1) # wird so nie geschrieben +# Attribute können zu jeder Zeit geändert werden +r1.name = "Calculon" +r1.say_hi() +# 2 eigene Klassen schreiben +# Circle und Rectangle +# radius +# Rectangle hat die Attribute: height, width +# beide Klassen haben die Funktionen: +# calc_area() # kreis: pi*radius**2 3.14 +# calc_perimeter() # 2 * pi * radius +import math +class Circle: + def __init__(self, radius): + self.radius = radius + self.area = 0 + + def calc_area(self): + self.area = 3.14 * self.radius ** 2 # ^,|,& -> binäre operatoren + return self.area + + def calc_perimeter(self): + return 2 * 3.14 * self.radius + +class Rectangle: + def __init__(self, width, height): + self.width = width + self.height = height + + def calc_area(self): + area = self.width * self.height + return area + + def calc_perimeter(self): + perimeter = 2 * (self.width + self.height) + return perimeter + +if __name__ == "__main__": + print("-"*100) + c1 = Circle(2) + c2 = Circle(3.2) + print(c1.radius) + area = c1.calc_area() # Circle.calc_area(c1) + print(area) + perimeter = c1.calc_perimeter() + print(perimeter) + print(c1.calc_area() > c2.calc_area()) + + print(c1.radius, c1.area) + c1.radius += 1 # daran denken die fläche zu berechnen + print(c1.radius, c1.area) + + r1 = Rectangle(3, 9) + print(r1.width) + print(r1.calc_area(), r1.calc_perimeter()) + + # inkorrekte Zustände + r1.width -= 12 + print(r1.calc_area(), r1.calc_perimeter()) + + + + + # rectangle ohne oop + r = [4, 2] # h, w oder w,h? + r = {"height": 4, "width": 2} \ No newline at end of file diff --git a/src/T04_Klassen_privat.py b/src/T04_Klassen_privat.py new file mode 100644 index 0000000..c858ce5 --- /dev/null +++ b/src/T04_Klassen_privat.py @@ -0,0 +1,99 @@ + +# PascalCase +class Robot: + # alle funktionen mit unterstrichen links und rechts müssen exakt so heißen + def __init__(self, name): + print(f"Creating a new robot named {name}") + # __ führt dazu das ein Attribut privat ist + # bedeutet, es kann nur innerhalb der Klasse genutzt werden + self.__name = name + self.set_name(name) + + def get_name(self): + return self.__name + + def set_name(self, name): + if len(name) < 6: + raise ValueError("RObot name has to have at least 6 characters") + self.__name = name + + def say_hi(self): + print(f"Hello, I'm a robot called {self.__name}") + + +r1 = Robot("Bender") # führt die init-funktion aus +print(r1.__dict__) +r1.build_year = 2996 +print(r1.__dict__) +print(r1.__dict__) + + + +print(r1.get_name()) +r1.say_hi() # r1 -> Robot -> Robot.say_hi(r1) +Robot.say_hi(r1) # wird so nie geschrieben +# Attribute können zu jeder Zeit geändert werden (nicht private) +r1.__name = "Calculon" +r1.say_hi() +# 2 eigene Klassen schreiben +# Circle und Rectangle +# radius +# Rectangle hat die Attribute: height, width +# beide Klassen haben die Funktionen: +# calc_area() # kreis: pi*radius**2 3.14 +# calc_perimeter() # 2 * pi * radius +import math +class Circle: + def __init__(self, radius): + self.__radius = radius + self.set_radius(radius) + self.__area = 0 + self.__area_computed = False + + def get_radius(self): + return self.__radius + + def set_radius(self, radius): + assert radius > 0, (f"Radius {radius}, was smaller 0") + self.__radius = radius + self.__area_computed = False + # area berechnen + + def get_area(self): + if self.__area_computed: + return self.__area + print("Calculting the area") + self.__area = 3.14 * self.__radius ** 2 # ^,|,& -> binäre operatoren + self.__area_computed = True + return self.__area + + def calc_perimeter(self): + return 2 * 3.14 * self.__radius + +class Rectangle: + def __init__(self, width, height): + self.width = width + self.height = height + + def calc_area(self): + area = self.width * self.height + return area + + def calc_perimeter(self): + perimeter = 2 * (self.width + self.height) + return perimeter + +if __name__ == "__main__": + print("-"*100) + c1 = Circle(2) + area = c1.get_area() # Circle.calc_area(c1) + print(area) + print() + print(c1.get_area()) + c1.set_radius(5) + print(c1.get_area()) + # schönen weg mit properties + c1.set_radius(c1.get_radius() + 1) + c1.radius += 3 # fehleranfällig + # beides verbinden -> properties + # als dataclasses! \ No newline at end of file diff --git a/src/T05_Klassen_properties.py b/src/T05_Klassen_properties.py new file mode 100644 index 0000000..960cc0a --- /dev/null +++ b/src/T05_Klassen_properties.py @@ -0,0 +1,52 @@ +class Circle: + def __init__(self, radius): + # keine normale Attributzuweisung + # versteckter Aufruf einer Funktion + self.radius = radius + + @property + def radius(self): + print("Getting the radius") + return self.__radius + + @radius.setter + def radius(self, radius): + print("Setting a new radius") + assert radius > 0, "Radius has to be positive" + self.__radius = radius + + @property + def diameter(self): + return 2 * self.radius + + @diameter.setter + def diameter(self, d): + self.radius = d / 2 + + @property + def area(self): + # keine teuren/langsamen operationen geschehen + return self.calc_area() + + def calc_area(self): + area = 3.14 * self.radius ** 2 + return area + + def calc_perimeter(self): + return 3.14 * self.diameter + + # muss str zurück geben + def __repr__(self): + return f"Circle(radius={self.radius})" + +if __name__ == "__main__": + c1 = Circle(2) + print(c1.radius) + c1.radius += 0.5 + print(c1.diameter) + c1.diameter -= 1 + print(c1) # -> entweder __str__ oder __repr__ definierens + c2 = Circle(2) + print(c1 == c2) + # beides verbinden -> properties + # als dataclasses! \ No newline at end of file diff --git a/src/T06_Klassen_dataclasses.py b/src/T06_Klassen_dataclasses.py new file mode 100644 index 0000000..c3fd6e3 --- /dev/null +++ b/src/T06_Klassen_dataclasses.py @@ -0,0 +1,46 @@ +from dataclasses import dataclass, field + +@dataclass +class Circle: + __radius: field(init=True) + + @property + def radius(self): + return self.__radius + + @radius.setter + def radius(self, r): + assert r > 0, "Error radius < 0" + self.__radius = r + + def __post_init__(self): + self.radius = self.__radius + print("Checking all attributes") + print(f"Checking {self.radius=}") + + # def __init__(self, radius): + # self.__radius = radius + # self.__post_init__() + +@dataclass(frozen=True) +class Rectangle: + width: float + height: float + +# automatisch ein __init__ +# automatisch ein __repr__ +# automatisch ein __eq__ + +if __name__ == "__main__": + c1 = Circle(2) + print(c1.radius) + c1.radius += 0.5 + print(c1.radius) + print(c1) + # beides verbinden -> properties + r1 = Rectangle(2, 5) + print(r1.width, r1.height) + r1 = Rectangle(r1.width + 2, r1.height) + d = {r1: "Ein rechteck"} + d[c1] = "Ein Kreis" # __hash__ + # als dataclasses! \ No newline at end of file diff --git a/src/T07_pandas.py b/src/T07_pandas.py new file mode 100644 index 0000000..ec79ae6 --- /dev/null +++ b/src/T07_pandas.py @@ -0,0 +1,113 @@ +import pandas as pd +# standardbibliothek +# Zwei Datenstrukturen +# Series (eine spalte) + +# Dem Dataframe + +cities = {"Stadt": ["London", "Berlin", "Madrid", "Rom", + "Paris", "Wien", "Bukarest", "Hamburg", + "Budapest", "Warsaw", "Barcelona", + "München", "Mailand"], + "Population": [8615246, 3562166, 3165235, 2874038, + 2273305, 1805681, 1803425, 1760433, + 1754000, 1740119, 1602386, 1493900, + 1350680], + "Land": ["England", "Deutschland", "Spanien", "Italien", + "Frankreich", "Österreich", "Romanien", + "Deutschland", "Ungarn", "Polen", "Spanien", + "Deutschland", "Italien"]} +# erstellung des dataframes +cities_df = pd.DataFrame(cities) + +# einfügen von neuen Spalten +areas = [1572, 892, 604, 1285, 105,415, 228, 755, 525, 517, 101, 310, 182] +# cities_df["Flaeche"] = areas # spalte an +print(cities_df) +# insert (position wo die spalte eingefügt wird, name, werte [skalar oder anzahl zeilen] +# insert(position, name, werte) +cities_df.insert(2, "Flaeche", areas) # wenn keine spalte namens flaeche existiert +# cities_df["Flaeche"] = -1 # keine überprüfung ob spalte existiert statt +print(cities_df) +print("-"*100) +# Setzen unseren eigenen Index +df2 = cities_df.set_index("Stadt", inplace=False) # standardwert +print(df2) +print(cities_df) +cities_df.set_index("Stadt", inplace=True) +print(cities_df) +print("\n", "*"*100) +# Selektion in Pandas +# spalten selektieren +print(cities_df["Population"]) +print("-" * 100) +# Selektion von Zeilen +# nach index +print(cities_df.loc["Paris"]) +# nach numerischen index +print() +print(cities_df.iloc[7]) +print() +# slicing +# slicing bei loc benötigt eine der 2 Bedingungen +# * index muss einzigartig sein +# * sortiert sein +print(cities_df.loc["Rom":"Warsaw"]) # inklusiv:inklusiv +# ändern den index auf land +# 1) Index zurücksetzen +cities_df.reset_index(inplace=True) +cities_df.set_index("Land", inplace=True) +print(cities_df) +print(cities_df.loc["England":"Polen"]) + +cities_df.sort_index(inplace=True) +print("-"*100) +print(cities_df) +print(cities_df.loc["Deutschland":"Italien"]) +print("-"*100) +print(cities_df.loc["Italien"]) +print("-"*100) +print(cities_df.iloc[0:5]) + + +cities_df = pd.DataFrame(cities) +cities_df["Flaeche"] = areas +print(cities_df) +cities_df.sort_values(by="Stadt", inplace=True) +print(cities_df) +print(cities_df.loc[4]) +print(cities_df.iloc[4]) +print("-"* 100) +print(cities_df) +# Selektion nach Information in Spalte +# boolsche Selektionsmaske +print(cities_df[cities_df["Population"] > 2e6]) +print(cities_df[cities_df["Population"] > 2_000_000]) + +# Maske selbst +print(cities_df["Population"] > 2_000_000) +print() +print("-" * 100) +print(cities_df[ + (cities_df["Population"] > 2_000_000) & + (cities_df["Flaeche"] > 1_000) + ]) # 0000000000001 & 1000010000010001 + +# spalten mit strings (contains unterstützt regulären ausdrücke) +print(cities_df[cities_df["Land"].str.contains("[E|e]n")]) +print(cities_df[cities_df["Land"] == "Spanien"]) +print() +print("-" * 100) +spanische_staedte = cities_df[cities_df["Land"] == "Spanien"] +spanien_info = spanische_staedte[["Population", "Flaeche"]] +print(spanien_info.sum()) + + +# & und +# | oder +# ^ exklusive_oder (1 + 1 == 0) +stadt_de_esp = cities_df[(cities_df["Land"] == "Spanien") | (cities_df["Land"] == "Deutschland")] +info = stadt_de_esp[["Population", "Flaeche"]] +print(info) +print(info.sum()) + diff --git a/src/T08_WorldCities.py b/src/T08_WorldCities.py new file mode 100644 index 0000000..ac024bb --- /dev/null +++ b/src/T08_WorldCities.py @@ -0,0 +1,25 @@ +import pandas +import pandas as pd + +world_cities = pd.read_excel("../data/worldcities.xlsx") +print(world_cities) +print(world_cities.columns) +print(f"{world_cities['city']}") + +# conda install openpyxl +# 1) Wie viele Einwohner haben alle Deutschen Städte? +# XYZ + +# 2) Wie viele Einwohner leben auf der nördlichen/Südlichen Halbkugel + +# 3) Welche Städte sind nördlich von Berlin und größer (Einwohner) als Berlin? +# New-York +# Istanbul +# Rom + +# funktion +# -> raise +# -> assert + +val = world_cities[(world_cities["city"] == "Berlin") & (world_cities["country"] == "Germany")]["population"].iloc[0] +print(type(val)) \ No newline at end of file