# -*- coding: utf-8 -*- """ Skript für "Einführung in Python" Hier steht normalerweise eine Gesamtbeschreibung des Python-Skriptes. Man kann es allerdings auch weglassen. Es ist rein optional. @author: Dr. David Krassnig """ # %% Typen in Python # Es gibt zwei Gesamtgruppen an Typen: Objekte und Methoden&Funktionen # Objekte: Beinhalten Informationen # Elementar: Text & Zahlen # Sammlungen: Objekte, die andere Objekte beinhalten # Methoden & Funktionen: Verarbeiten Informationen # Funktion: Eigenständig; stehen alleine; Input und Optionen werden in runden Klammern angegeben und durch Kommas getrennt (falls mehr als 1 Input): funktion(input), können aber je nach Funktion auch ohne Input genutzt werden: funktion() # Methoden: Fest an bestimmte Objektarten gebunden; werden durch einen Punkt direkt nach dem Objekt eingeführt. Verhalten sich sonst wie Funktionen. # tl;dr: Unterschied zwischen Funktionen & Methoden # Funktionen: Stehen eigenständig und erhalten (normalerweise) Input # Methoden: Stehen IMMER hinter einem Objekt und sind Objektabhängig # %% Objekte (Elementar) # Textinformationen werden als str (Strings) abgespeichert "Hallo Welt!" # Dies ist ein Text. # Numerische Informationen werden entweder als int (Integer) oder als float gespeichert 10 # Ganze Zahlen OHNE Nachkommastellen werden als int gespeichert 10.1 # Zahlen MIT Nachkommastellen werden als int gespeichert # ACHTUNG! int-Zahlen sind immer 100% präzise, aber float-Zahlen sind nur Annäherungen an den richtigen Wert! Ein Beispiel hierfür gibt es in der übernächsten Zelle! # # Man kann floats generell ohne Probleme nutzen, wenn einen die Genauigkeit von Zahlen jenseits von 7-10 Stellen nach dem Komma nicht interessiert (am besten auf die gewünschte Nachkommastelle runden; machen wir erst später). # # Erklärung für Interessierte: Zahlen werden binär abgespeichert (z.B. 01101). Das heißt der Computer nutzt Basis-2 (binär). Aber im binären System können manche Brüche nicht komplett dargestellt werden und erzeugen unendliche Zahlenketten. Dasselbe passiert bei anderen Zahlen auch in unserem normalen, dezimalen (Basis-10) System: 1/3=0.333333333333333333333... etc. Im Duodezimalsystem (Basis-12) hingegen gilt 1/3=0.4. Umgekehrt ist in unserem Standardsystem 1/10=0.1, der äquivalente Bruch im binären System hingegen unendlich lang. Da nicht unendlich viele Zahlen abgespeichert werden können, schneidet Python früher oder später die Kette ab, was zu Ungenauigkeiten führt. # %% Objekte anzeigen lassen mit Funktion # Bisher haben wir Objekte kreiert, diese werden aber NICHT beim Aufruf des Skriptes angezeigt. Um Informationen anzuzeigen, müssen wir eine Funktion nutzen: print(...) print("Dies ist ein Text.") # Text der angezeigt wird print(10) # Ganze Zahl die angezeigt wird print(10.1) # Float-Zahl die angezeigt wird # %% Schwachstelle von Floats veranschaulicht print(0.7+0.3) print(0.7+0.1+0.1+0.1) # Die zwei Zahlen sollten identisch sein, sind sie aber nicht. Da vermehrte Nutzung von impräzisen 0.1-floats zu einer Verzerrung des Ergebnisses führt. # Man kann dies bereinigen, indem man den float aufrunden lässt auf eine gewünschte Nachkommastelle. Hierfür kann round(zahl, nachkommastelle) genutzt werden. print(round(0.7+0.1+0.1+0.1, 2)) # %% Typen identifizieren # Um Typen zu identifizieren, kann man die Funktion type(...) nutzen: print(type("Dies ist ein Text.")) print(type(10)) print(type(10.1)) print(type("10")) # Zahlen werden mit Anführungszeichen wie jeder andere Text behandelt, was man am Typ sehen kann # %% Print mit mehr als einem Input # Die Funktion print(...) kann durch Kommas mehrere Argumente nehmen: print("Dies ist ein Text.","| Typ:",type("Dies ist ein Text.")) print(10,"| Typ:",type(10)) print(10.1,"| Typ:",type(10.1)) print("10","| Typ:",type("10")) # Zahlen werden mit Anführungszeichen wie jeder andere Text behandelt # %% Objekte (Sammlungen) # Es gibt mehrere Sammlungstypen, aber alle können mehrere Objekte beinhalten (inkl. weitere Sammlungen). Es gibt: # Tupel (geordnet, unveränderbar, Doppeleinträge möglich) # Listen (geordnet, veränderbar, Doppeleinträge möglich) # Menge (ungeordnet, veränderbar, KEINE Doppeleinträge) # Dictionary (ungeordnet, veränderbar, KEINE Doppeleinträge, besteht aus Relationen zwischen zwei Objekten (Schlüssel/Key & Wert/Value)) # # Die verschiedenen Sammlungstypen verbrauchen unterschiedlich viel Arbeitsspeicher (was mit heutigen RAM-Größen für die meisten von euch wenig relevant sein wird). # Die Hierarchie ist aufsteigend wie folgt: Tupel < Menge < Liste < Dictionary # Es ist gutes Design, so wenig Ressourcen zu verbrauchen wie möglich (unter Erfüllung all der gewünschten Voraussetzungen). # %% Tupel # Tupel werden genutzt, wenn man eine statische Sammlung haben möchte, die doppelte Elemente beinhalten können soll und/oder deren Reihenfolge relevant ist. # Wird also hauptsächlich genutzt, wenn eine Objekt-Kombination sich im Nachhinein garantiert nicht verändern soll (z.B. Parameter eines Experiments). # # Tupel werden mit runden Klammern (...) erschaffen print((2,1,"Risiko")) # Hier ein dreistelliges Tupel print(type((2,1,"Risiko"))) print() # Ohne Input produziert print eine Leerzeile # Tupel können doppelte Werte enthalten: print((2,2,2,2,1,"Risiko")) print("") # Tupel können auch weitere Sammlungen enthalten: print((2,1,"Risiko",("a","b","c"))) print("") # Tupel sind geordnet! print("Sind zwei Tupel mit unterschiedlicher Anordnung identisch?") print((2,1,"Risiko") == (1,2,"Risiko")) print() # Ohne Input produziert print eine Leerzeile # Tupel können im Nachhinein NICHT modifiziert werden! # %% Listen # Listen werden genutzt, wenn man eine dynamische Sammlung haben möchte, die doppelte Elemente beinhalten können soll und/oder deren Reihenfolge relevant ist. # # Listen werden mit echkigen Klammern [...] erschaffen print([2,1,"Risiko"]) print(type([2,1,"Risiko"])) print() # Ohne Input produziert print eine Leerzeile # Listen können doppelte Werte enthalten: print([2,2,2,2,1,"Risiko"]) print("") # Listen können auch weitere Listen enthalten: print([2,1,"Risiko",["a","b","c"]]) print("") # Listen sind geordnet! print("Sind zwei Listen mit unterschiedlicher Anordnung identisch?") print([2,1,"Risiko"] == [1,2,"Risiko"]) print() # Ohne Input produziert print eine Leerzeile # Listen können im Nachhinein modifiziert werden! # Macht man normalerweise mit der Methode [...].append(...), aber die können wir erst nutzen, wenn wir Variabeln haben :-( # %% Mengen # Mengen werden genutzt, wenn man eine dynamische Sammlung haben möchte, wo einen die Frequenz der Objekte nicht interessiert und deren Reihenfolge auch nicht (z.B. wenn man nur eine vollständige Liste aller genutzten Namen haben möchte, ohne Namensfrequenz messen zu wollen). # # Mengen werden mit geschweiften Klammern {...} erschaffen print({2,1,"Risiko"}) print(type({2,1,"Risiko"})) print() # Ohne Input produziert print eine Leerzeile # # Mengen ignorieren doppelte Werte print({2,2,2,2,2,1,"Risiko"}) print() # Ohne Input produziert print eine Leerzeile # Mengen können auch weitere Sammlungen enthalten, aber KEINE Listen oder Mengen: # print({2,1,"Risiko",{"a","b","c"}}) # Das würde einen Fehler ergeben # print({2,1,"Risiko",["a","b","c"]}) # Das würde auch einen Fehler ergeben print({2,1,"Risiko",("a","b","c")}) # Das funktioniert print("") # Mengen sind NICHT geordnet! print("Sind zwei Mengen mit unterschiedlicher Anordnung identisch?") print({2,1,"Risiko"} == {1,2,"Risiko"}) print() # Ohne Input produziert print eine Leerzeile # Mengen können im Nachhinein modifiziert werden! # Macht man normalerweise mit der Methode {...}.add(...), aber die können wir erst nutzen, wenn wir Variabeln haben :-( # %% Dictionaries # Dictionaries werden genutzt, wenn man eine Sammlung an Relationen haben möchte. # # Dictionaries werden mit geschweiften Klammern {...} erschaffen und unterscheiden sich von Mengen darin, dass jedes Element aus zwei Objekten besteht, welche mit einem Doppelpunkt miteinander verbunden werden. # Die linke Seite nennt man Schlüssel und die rechte Seite Wert. print("Ein Dictionary, welches identifiziert, wer ein Superheld ist:") print({"Darkwing Duck":True,"Negaduck":False,"Superman":True,"Guybrush Threepwood":True}) print(type({"Darkwing Duck":True,"Negaduck":False,"Superman":True,"Guybrush Threepwood":True})) print() # Ohne Input produziert print eine Leerzeile # # Dictionaries können keine doppelte Schlüssel besitzen. Es wird immer nur der letzte übernommen. print({"Superman":False,"Superman":"Ihr werdet niemals diesen Wert sehen :-)","Superman":True}) print() # Ohne Input produziert print eine Leerzeile # Dictionaries können als Werte auch weitere Sammlungen besitzen. print({1:("a","b","c"),2:"Ich bin eine Zahl!"}) # Das funktioniert print() # Ohne Input produziert print eine Leerzeile # Besonders praktisch: Dictionaries können zu weiteren Dictionaries führen, womit man eine Hierarchie an Informationen aufbauen kann: print({"Anna":{"Alter":16,"Geschlecht":"weiblich"},"David":{"Alter":69,"Geschlecht":"männlich"},"Pikachu":{"Alter":4,"Geschlecht":"unbekannt"}}) print() # Ohne Input produziert print eine Leerzeile # Auch praktisch: man kann bei größeren Sammlungen das ganze mit Zeilenbrüchen übersichtlicher formatieren (was aber keinen Einfluss auf den Output hat): print({ "Anna": {"Alter":16,"Geschlecht":"weiblich"}, "David": {"Alter":69,"Geschlecht":"männlich"}, "Pikachu":{"Alter":4, "Geschlecht":"unbekannt"} }) print() # Ohne Input produziert print eine Leerzeile # Dictionaries sind NICHT geordnet! print("Sind zwei Dictionaries mit unterschiedlicher Anordnung identisch?") print({1:("a","b","c"),2:"Ich bin eine Zahl!"} == {2:"Ich bin eine Zahl!", 1:("a","b","c")}) print() # Ohne Input produziert print eine Leerzeile # Dictionaries können im Nachhinein modifiziert werden! # Können wir auch hier erst machen, wenn wir Variabeln nutzen. # %% Dictionary-Werte abrufen # # Man kann über eckige Klammern DIREKT nach dem Dictionary einen Schlüssel angeben, für den ein Wert abgerufen wird: {...}[Schlüssel] print({"Anna":"weiblich","David":"männlich"}) print({"Anna":"weiblich","David":["männlich"]}["David"]) # Dies kann genutzt werden um Outputs wie diesen hier zu kreieren: print("Davids Geschlecht:", {"Anna":"weiblich","David":"männlich"}["David"]) print() # Ohne Input produziert print eine Leerzeile # Dies kann man auch mit Dictionaries machen, die auf Dictionaries führen. Hierbei werden die respektiven Schlüssel nacheinenader in eigenen eckigen Klammern aufgelistet: {...}[Schlüssel 1][Schlüssel 2] print("Pikachus Alter:",{ "Anna": {"Alter":16,"Geschlecht":"weiblich"}, "David": {"Alter":69,"Geschlecht":"männlich"}, "Pikachu":{"Alter":4, "Geschlecht":"unbekannt"} }["Pikachu"]["Alter"]) print() # Ohne Input produziert print eine Leerzeile # Diese Möglichkeit wird besonders praktisch, wenn wir Dictionaries in Variabeln abspeichern oder Schlaufen nutzen möchten. # %% Tupel/Listen-Werte abrufen # Ähnlich kann man auch für Tupel und Listen die einzelnen Werte abrufen. Da diese geordnet sind, benutzt man hierfür eine Index-Zahl. Jeder Wert in einem Tupel hat einen unsichtbaren Index. Der erste Wert hat einen Index von 0. Der zweite Wert hat einen Index von 1 (etc.). Insgesamt gilt, dass man für das n-te Objekt in einer Liste/einem Tupel den Index n-1 aufrufen muss: print("Erster Wert von",[2,1,"Risiko"]) print([2,1,"Risiko"][0]) print("Dritter Wert von",[2,1,"Risiko"]) print([2,1,"Risiko"][2]) print() print("Erster Wert von",(2,1,"Risiko")) print((2,1,"Risiko")[0]) print("Dritter Wert von",(2,1,"Risiko")) print((2,1,"Risiko")[2]) # %% Einzelne Charakter eines Strings abrufen # Wie bei Tupel/Listen können für Strings einzelne Charakter aufgerufen werden. Man kann hierbei Strings als eine geordnete Liste an Charakteren betrachten. print("Erster Buchstabe von", "Xena") print("Xena"[0]) print() print("Dritter Buchstabe von", "Xena") print("Xena"[2]) # %% Slicing # Anstatt einzelne Werte für Tupel/Listen/Strings aufzurufen, kann man auch definieren, von welchem bis zu welchem Wert etwas abgerufen werden soll. Hierzu benutzt man innerhalb der eckigen Klammern einen Doppelpunkt [x:y] (x=Anfangszahl, y=Endzahl (Endwert selber wird NICHT inkludiert)). Wenn x oder y weggelassen wird, wird stattdessen bis zur respektiven Grenze des Strings/des Tupels/der Liste weitergemacht. # Hinten abschneiden print("Die ersten zwei Werte von",[2,1,"Risiko","Darkwing Duck"]) print([2,1,"Risiko","Darkwing Duck"][:2]) print() print("Die ersten zwei Werte von",(2,1,"Risiko","Darkwing Duck")) print((2,1,"Risiko","Darkwing Duck")[:2]) print() print("Die ersten fünf Werte von","Superkalifragilistikexpialigetisch") print("Superkalifragilistikexpialigetisch"[:5]) print() # Vorne abschneiden print("Alle Werte nach dem ersten von",[2,1,"Risiko","Darkwing Duck"]) print([2,1,"Risiko","Darkwing Duck"][1:]) print() print("Alle Werte nach dem ersten von",(2,1,"Risiko","Darkwing Duck")) print((2,1,"Risiko","Darkwing Duck")[1:]) print() print("Alle Werte nach dem zwölften von","Superkalifragilistikexpialigetisch") print("Superkalifragilistikexpialigetisch"[12:]) print() # Vorne UND hinten abschneiden print("Die Mitte von",[2,1,"Risiko","Darkwing Duck"]) print([2,1,"Risiko","Darkwing Duck"][1:3]) print() print("Die Mitte von",(2,1,"Risiko","Darkwing Duck")) print((2,1,"Risiko","Darkwing Duck")[1:3]) print() print("Eines der untergeordneten 'Worte' von","Superkalifragilistikexpialigetisch") print("Superkalifragilistikexpialigetisch"[5:20]) # Fun Fact: Man kann auch negative Werte nutzen. Dann wird von rechts aus gezählt. print("Die letzten zwei Werte von",[2,1,"Risiko","Darkwing Duck"]) print([2,1,"Risiko","Darkwing Duck"][-2:]) print() print("Die letzten zwei Werte von",(2,1,"Risiko","Darkwing Duck")) print((2,1,"Risiko","Darkwing Duck")[-2:]) print() print("Die letzten zehn Werte von","Superkalifragilistikexpialigetisch") print("Superkalifragilistikexpialigetisch"[-10:]) # ====================================================================== # %% ÜBUNGEN # %% Welcher Typ sollte jeweils genutzt werden? # 1. Name eines Kursteilnehmers # 2. Anzahl der Kursteilnehmer # 3. Relativer Anteil der Online-Kursteilnehmer # 4. Namensliste aller Kursteilnehmer # 5. Liste aller einzigartigen Vornamen im Kurs # 6. Teilnehmerliste, die online/präsenz für jede Person anzeigt # %% "Blitzo" mag seinen Namen nicht und möchte daher das "o" am Ende weglassen. Wie geht das? # %% Ihr habt eine Menge an Dateien die alle wie folgt benannt sind: "proband_YYYY-MM-DD_observation.csv". # a.) Wie kann man das Dateiende vom String entfernen? # b.) Wie kann man das Datum aus dem String extrahieren? # %% Elemente von Listen können via [n] aufgerufen werden und die einzelnen Werte eines Dictionaries via [key]. Kann man ähnliches mit Mengen machen? Falls ja, wie? Falls nein, warum nicht? # ======================================================================