Tag 3: Funktionen & Standardbibliothek#
Bisher haben wir einzelne Code-Stücke geschrieben und nacheinander ausgeführt.
Aus den vorherigen Tagen sind Sie bereits mit einigen Funktionen wie print()
, len()
oder range()
vertraut.Python bietet viele eingebaute Funktionen wie diese, aber Sie können auch Ihre eigenen Funktionen schreiben.
Eine Funktion ist wie ein Miniprogramm innerhalb eines Programms. Funktionen können genutzt werden:
um komplexere Abschnitte zu kapseln
um wiederholt verwendete Funktionalität nicht mehrfach im Code zu haben
um Programme zu strukturieren
um anderen Leuten Funktionalität zur Verfügung zu stellen
…
Im Folgenden führen wir in Funktionen ein und wiederholen oder klären damit teilweise Inhalte der letzten Tage.
Funktionen#
Funktions-Definition#
Mit dem Schlüsselwort def
können Funktionen definiert werden, gefolgt von einem Namen und eventuell Parametern.
Das folgende Beispiel definiert eine Funktion namens hello_world()
.
def hello_world():
print('Hi')
Formalisiert wäre eine Funktion:
def funktionsname(parameter):
""" docstring - Beschreibung"""
# statement(s)
return value # optional
funktionsname(parameter) # Funktionsaufruf
Der erste Teil def funktionsname(parameter):
wird auch Header genannt. Der eingerückte Teil wird Body genannt. Funktionen ermöglichen wiederholtes Nutzen von Funktionalitäten und das Teilen dieser Funktionalitäten mit Anderen. Zudem kann Code so besser organisiert und wiederverwendet werden - wie oben beschrieben.
Parameter und Rückgabewerte:#
Funktionen können Parameter akzeptieren und Werte zurückgeben, die mit dem return
-Statement definiert werden.
Die folgende Funktion addition
akzeptiert zwei Parameter x
und y
und gibt das Resultat von x + y
zurück.
def addition(x, y):
return x + y
Um besser zu verstehen wie Funktionen funktionieren und wie wir selber welche schreiben können, erstellen wir nun eine einfache erste Version.
def hello(): # 'def' definiert eine Funktion, hello_world ist der Name der Funktion
print('Hi')
print('Guten Tag')
hello() # mit dem Namen und Klammern wird eine Funktion aufgerufen
# Ausgabe
# Hi
# Guten Tag
Hierbei wird die Funktion mit dem Schlüsselwort def
eingeleitet.
Der Funktion wird der Name hello_world
gegeben und es werden keine Parameter übergeben (()
).
Durch die folgende Einrückung der print()
-Aufrufe, ist klar, was zu Funktion gehört und was nicht.
Der Funktionsaufruf hello()
ist nicht mehr eingerückt und gehört somit nicht mehr zur Funktion.
Dieser ruft dann die Funktion auf.
Wichtig ist, dass Funktionen erst definiert werden müssen, bevor sie aufgerufen werden können.
Das heisst, die Funktion muss im Code ‘über’ dem Aufruf stehen.
Die Möglichkeit, Funktionen Parameter “übergeben” zu können ermöglicht es beispielsweise auf Grundlage dessen Entscheidungen in der Funktion treffen zu können. So kann zum Beispiel entschieden werden, ob höflich oder leger gegrüßt werden soll.
def hello(hoeflich):
if hoeflich == True:
print('Guten Tag')
else:
print('Yo!')
hoeflich = True
hello(hoeflich) # Funktionsaufruf mit Parameter
# Ausgabe
# Guten Tag
Die Funktion von oben akzeptiert nun einen Parameter hoeflich
. Dieser wird in dem if
-else
-Ausdruck verwendet um zu unterscheiden ob höflich gegrüßt wird (True
) oder leger (False
). Wichtig ist, dass im Aufruf hello(hoeflich)
der Parameter entsprechend übergeben wird. Dieser muss nicht den selben Namen aufweisen - hello(True)
würde genauso funktionieren.
Kommen wir nun zu dem Eingangsbeispiel der Addtion wieder.
def addition(x, y):
add = x + y
return add
Diese Funktion ermöglicht uns, eine einfache Addition wiederzuverwenden.
Diese Funktion haben wir natürlich schon als Operator +
, jedoch könnten in dieser Funktion beispielweise noch unterschiedliche Überprüfungen hinzugefügt werden
So könnte geprüft werdten, dass es sich um einen Integer handeln muss, und mit Fehlern automatisch umgegangen werden (kommende Tage).
Im Unterschied zur vorherigen Funktion gibt diese Funktion mit dem Schlüsselwort return
etwas “zurück”.
In diesem Fall die “Summe” von x
und y
.
Da die Funktion etwas zurückgibt, kann mit ihrem Aufruf einer Variable ein neuer Wert zugewiesen werden.
Beispiel:
a = 5
b = 10
summe = addition(a, b)
Hier werden zwei Variablen a
und b
mit den Werten 5
und 10
initialisiert und als Parameter der Funktion addition
übergeben.
Diese werden in der Funktion der Reihenfolge nach den lokalen Variablen x
(a
) und y
(b
) zugewiesen.
In der Funktion werden diese dann addiert und ‘zurückgegeben’.
Damit wird mit der errechneten Summe der Addition in der Funktion nun die Variable summe
initialisiert. summe
hat somit den addierten Wert der beiden Variablen a
und b
.
Bonusinhalt: Globale und lokale Variablen
Globale und lokale Variablen unterscheiden, wo welche Variable wie zugeordnet ist.
Im obigen Beispiel ist die Variable hoeflich
, die außerhalb der Funktion hello()
definiert ist, eine globale Variable.
Innerhalb der Funktion ist hoeflich
eine lokale Variable mit gleichem Namen.
Globale Variablen: werden außerhalb von Funktionen definiert und können von jeder Funktion im Programmcode verwendet werden.
In diesem Beispiel wird hoeflich
als globale Variable vor dem Funktionsaufruf mit dem Wert True
initialisiert.
Lokale Variablen: werden innerhalb einer Funktion definiert und sind nur innerhalb dieser Funktion sichtbar.
Das ist zum Beispiel bei Parametern von Funktionen der Fall.
In diesem Beispiel ist hoeflich
auch der Parameter der Funktion hello()
.
Diese lokale Variable hat Vorrang vor der globalen Variable mit dem gleichen Namen.
Die Zuweisung von True
oder False
zu art_der_begruessung
innerhalb der Funktion hello()
beeinflusst nur die lokale Variable innerhalb des Funktionskontexts.
Sie müssen das nicht direkt komplett verstanden haben - wir kommen darauf zurück. Halten Sie es allerdings im Kopf.
Erforderliche und Optionale Parameter#
Funktionen können optionale Parameter haben, die beim Funktionsaufruf nicht mit übergeben werden müssen.
Ein Beispiel haben Sie bereits kennengelernt, nämlich reverse=True
in der list.sort()
-Funktion.
Dazu definieren wir einen Standard-Wert für den Parameter im Funktions-Header:
def hallo(sprache, nett=True):
if sprache == 'DE':
if nett:
return "Moin"
else:
return "MoinMoin"
else:
if nett:
return "Hello"
else:
return "HelloHello"
In diesem Beispiel hat die Funktion hallo()
einen erforderlichen Parameter (den ersten, nämlich sprache
und einen optionalen Parameter (den zweiten, nämlich nett
). Für den Aufruf haben wir also zwei Möglichkeiten:
hallo('EN') # Gibt "Hello" zurück, "nett" hat den Standard-Wert von "True"
hallo('DE', False) #Gibt "MoinMoin" zurück
Im ersten Fall wird sprache
beim Funktionsaufruf den Wert EN
annehmen, nett
den Standard-Wert von True
. Im zweiten Fall werden beide Parameter übergeben, nett
hat also den Wert False
.
Wird die Funktion allerdings ohne Argument aufgerufen, erhalten wir einen Fehler:
>>> hallo()
Traceback (most recent call last):
File "<input>", line 1, in <module>
hallo()
TypeError: hallo() missing 1 required positional argument: 'sprache'
Python-Standardbibliothek#
Die Python-Standardbibliothek, auch bekannt als STDLIB, ist eine Sammlung von Modulen und Paketen, die mit Python geliefert werden und eine Vielzahl von nützlichen Funktionen und Werkzeugen für verschiedene Aufgaben bieten. Diese Bibliothek ist Teil der Python-Installation und muss nicht separat installiert werden.
Hier sind einige der Hauptbereiche, die von der Python-Standardbibliothek abgedeckt werden und was man damit machen kann:
Betriebssystemoperationen (
os
): Dasos
-Modul bietet Funktionen zum Arbeiten mit dem Betriebssystem, einschließlich Dateioperationen, Verzeichnisoperationen, Umgebungsvariablen und Prozesssteuerung (morgen)Mathematische Funktionen (
math
): Dasmath
-Modul enthält eine Vielzahl von mathematischen Funktionen und Konstanten, die für mathematische Berechnungen nützlich sind. Dazu gehören Funktionen für Trigonometrie, Logarithmen, Exponentialfunktionen, Runden und mehr.Zufallszahlen (
random
): Dasrandom
-Modul ermöglicht die Erzeugung von Pseudozufallszahlen. Es bietet Funktionen zum Generieren von Zufallszahlen, zur Auswahl von Zufallselementen aus Listen und zur Mischen von Sequenzen.Datum und Zeit (
datetime
): Dasdatetime
-Modul bietet Klassen und Funktionen zum Arbeiten mit Datum und Zeit. Es ermöglicht das Erstellen, Manipulieren und Formatieren von Datum und Zeit sowie die Berechnung von Zeitdifferenzen.Dateiverarbeitung (
io
): Dasio
-Modul bietet Tools zum Lesen und Schreiben von Dateien und Datenströmen. Es ermöglicht das Arbeiten mit Dateien im Speicher sowie das Umleiten von Ein- und Ausgaben.Reguläre Ausdrücke (
re
): Dasre
-Modul stellt Funktionen und Klassen zum Arbeiten mit regulären Ausdrücken bereit. Reguläre Ausdrücke werden verwendet, um Textmuster in Zeichenketten zu suchen und zu manipulieren.Datenkompression (
gzip
,zipfile
,zlib
): Python bietet verschiedene Module zur Datenkompression, einschließlichgzip
für GZIP-Komprimierung,zipfile
für ZIP-Archive undzlib
für die Verwendung des Zlib-Komprimierungsformats.Netzwerk- und Internetprotokolle (
socket
,urllib
): Diesocket
- undurllib
-Module ermöglichen die Kommunikation über Netzwerke und das Arbeiten mit Internetprotokollen wie HTTP, FTP und SMTP.
Diese Module müssen vor der Nutzung importiert werden. Das schauen wir uns am letzten Tag an.
Darüber hinaus bietet Python eine Reihe an sogenannten Methoden, die auf den eingebauten Datentypen operieren.
Beispiele hierfür sind list.pop()
oder list.remove()
.
Eine Methode ist auch eine Funktion, die allerdings an einen spezifischen Datentyp, ein Objekt oder eine Klasse (hierzu später mehr) gebunden ist.
Methoden nutzen immer implizit den Wert oder das Objekt, dem sie zugeordnet sind.
Das bezeichnen wir auch als den Wert, auf dem sie aufgerufen werden und das ist das, was vor dem .
steht als Parameter.
Dies sind nur einige Beispiele für die Funktionen und Methoden, die in der Python-Standardbibliothek enthalten sind. Schauen Sie in die Dokumentation für mehr Informationen. Meistens ist es schneller, einfacher und besser solche Funktionen zu nutzen, als für den selben Zweck eigene Funktionen zu schreiben.
Tüftelspaß und Knobeleien#
Das war’s auch schon mit neuem Inhalt für heute. Jetzt ist Zeit zum Fragen stellen! Stellen Sie gern Fragen zu jeglichen Inhalten der letzten Tage - direkt im Plenum oder wie gehabt im allg. Markdown-Dokument.
Tipp
Besprechen Sie sich erst in der Gruppe wie was wo wann gemacht werden sollte. Sie dürfen eng zusammenarbeiten, gern auch im Pair-programming, jedoch sollte letztlich jede*r alles verstanden haben.
Erste Tüfteleien
Schreiben Sie eine Funktion
division
, die zwei Variablen nimmt, verarbeitet und das Ergebnis aus- und zurückgibt. Berechnen Sie mittels dieser Funktion das Ergebnis von: 8958937768937 geteilt durch 2851718461558. Was kommt raus?Schreiben Sie eine Funktion
multiplikation
und errechnen Sie mit dieser Funktion, wieviele Sekunden ein Jahr mit 365 Tagen hat.Tipp
Berechnen Sie erst die Sekunden pro Minute, dann pro Stunde und dann pro Tag
Nutzen Sie die Methoden der Standardbibliothek für Strings und finden Sie heraus, wieviele Buchstaben das Wort
'Donaudampfschifffahrtselektrizitätenhauptbetriebswerkbauunternehmenbeamtengesellschaft'
hat. Welcher Vokal ist am häufigsten vertreten? Wie oft?
Schreiben Sie für jede dieser Zählungen oder Vergleiche eine eigene Funktion.Tipp
Tipp: Es können auch Funktionen innerhalb von Funktionen aufgerufen werden.
Tipp
Die Funktionen zur Bestimmung der Länge eines Strings und zum Zählen von Buchstaben zählen sind schon in der Standardbibliothek definiert und können von Ihnen wiederverwendet werden.
Built in String Methods Built in String Methods - etwas übersichtlicher - dafür jedoch nicht vollständigAuflösung: Versuchen Sie es erst selbst!
s = "hallo" len(s) s.count('vokal')
Wie viele Buchstaben hat die ausgeschriebene Version der chemischen Formula Titin im Englischen? Welcher Buchstabe ist hier der häufigste? Nutzen Sie gern Ihre in Aufgabe 3 geschriebenen Funktionen.
Fortgeschrittenes Werkeln
Machen Sie sich mit der Extra-Aufgabe am Ende von Tag 2 vertraut. Diese können Sie im Anschluss gern noch bearbeiten, gehen Sie dann jedoch bitte als Gruppe gemeinsam zu diesen Aufgaben über. Das Ziel dieser Aufgabe ist es, für alle Städte die Einwohnerdichte herauszufinden. Zunächst müssen jedoch die Daten “gereinigt” werden, also für die entsprechende Aufgabe so vorbereitet werden, dass eine Verarbeitung möglich ist.
Tipp:
Versuchen Sie immer erst nur mit einem Wert eine Lösung zu finden und weiten Sie dann diese Lösung auf die gesamte Liste an.
Sie finden in der Datengrundlage zwei Listen mit Informationen zu Flächen und Einwohnerzahlen. Diese sind ergänzende Informationen zu den Städten und Ländern der Extra-Aufgabe von gestern.
Datengrundlage
Achtung! Reihenfolge sollte nicht (ohne Überlegung) verändert werden, da die Listen sonst nicht mehr zueinander passen!
flaeche = ['0219 km²', '0012 km²', '0362 km²', '0360 km²', '0892 km²', '0062 km²', '0368 km²', '0161 km²', '0525 km²', '0228 km²', '0120 km²', '0007 km²', '0119 km²', '0715 km²', '0839 km²', '0077 km²', '0085 km²', '0275 km²', '1.572 km²', '0052 km²', '0606 km²', '0305 km²', '0002 km²', '1.081 km²', '0111 km²', '0459 km²', '0105 km²', '0108 km²', '0496 km²', '0275 km²', '0307 km²', '1.285 km²', '0142 km²', '0571 km²', '492 km²', '0187 km²', '0159 km²', '0042 km²', '0017 km²', '0001 km²', '0000,44 km² 6', '0402 km²', '0518 km²', '0415 km²', '0641 km²'] einwohnerzahl = ['872.757 (2019)', '23.498 (2020)', '664.046 (2011)', '1.344.844 (2016)', '3.664.088 (2020)', '134.794 (2020)', '440.948 (2020)', '1.067.557 (2009)', '1.697.343 (2005)', '1.821.000 (2018)', '593.800 (2006)', '4.376 (2008)', '506.211 (2006)', '559.330 (2004)', '2.687.610 (2005)', '569.557 (2014)', '517.802 (2005)', '278.638 (2007)', '7.421.209 (2007)', '115.227 (2016)', '3.155.399 (2005)', '1.741.371 (2004)', '36.136 (2012)', '11.503.501 (2010)', '276.410 (2012)', '613.285 (2012)', '2.220.445 (2014)', '143.718 (2008)', '1.181.610 (2005)', '111.721 (2007)', '731.672 (2005)', '2.547.932 (2005)', '304.065 (2007)', '474.019 (2003)', '1.307.439 (2021)', '766.747 (2005)', '403.505 (2006)', '600.339 (2006)', '5.109 (2006)', '6.300 (2005)', '932 (2005)', '553.904 (2006)', '1.864.679 (2021)', '1.931.593 (2022)', '780.097 (2005)']
Fügen Sie zunächst die Daten in Ihren Code ein und schauen Sie sich die Datengrundlage genauer an. Was muss mit den Daten passieren, damit Sie mit ihnen eine Einwohnerdichte berechnen können?
Datenbeschaffung und -bereinigung
Datenbeschaffung und -bereinigung sind häufig große Teile im wissenschaftlichen Arbeiten. Daten in der freien Wildbahn können komplex, schwer zu verstehen, unvollständig und fehlerhaft sein. Nur selten sind Daten direkt verarbeitbar. Die Bereinigung, also die Überprüfung und Vorbereitung der Daten für die gewollte Verarbeitung, ist somit normaler Teil wissenschaftlicher Praxis. Diesen Schritt werden Sie nun im Folgenden auch durchlaufen.
Für die Fläche werden wir dies nun gemeinsam durchgehen, für die Einwohnerzahl ist es dann Ihnen überlassen, eine Lösung zu finden. Initialisieren Sie nun mit dem ersten Element der Flächen-Liste eine neue Variable und geben Sie sie aus. Bei der Fläche ist dies der Wert
'0219 km²'
an der Indexstelle ‘0’ der Liste. Um letztlich eine Berechnung von Fläche und Einwohnerzahl zu ermöglichen, müssen die Daten im Datentyp ‘float’ vorliegen. Die Daten liegen bisher aber noch als ‘String’ vor und sind somit noch nicht numerisch zu verarbeiten. Den Prozess einen Datentyp in einen anderen Datentyp zu verwandeln nennt sich casten. Das kann üblicherweise durchgeführt werden, indem der gewünschte Datentyp als Funktion auf den vorliegenden Werten ausgeführt wird:f1 = '0219' wert = float(f1)
Probieren Sie dies gern im REPL aus und schauen was passiert.
Casting von Datentypen
“Casting” ist der Prozess, bei dem ein Datentyp in einen anderen Datentyp umgewandelt wird. Wenn Sie zum Beispiel eine Zahl als Text haben, sie aber als Zahl verwenden möchten, müssen Sie sie “casten”, also in einen numerischen Datentyp umwandeln.
Ein einfaches Beispiel ist das “Casten” eines Strings in eine Ganzzahl:
zahl_als_text = "123" zahl_als_zahl = int(zahl_als_text)
Hier wird der String “123” in die Ganzzahl 123 umgewandelt. Dies ermöglicht es Ihnen, mathematische Operationen mit der Zahl durchzuführen, wie zum Beispiel
zahl_als_zahl * 2
.Versuchen Sie nun herauszufinden, woran es liegt, dass der String nicht in einen Gleitkommadatentyp (float) überführt werden kann. Das können Sie versuchen, indem Sie einzelne Teile des Wertes versuchen zu casten - so können Sie das Problem genauer definieren.
Sie werden erkannt haben, dass einige Inhalte des Strings nicht konvertierbar sind. Anderes funktionieren jedoch überraschenderweise. Versuchen Sie dies nun auch mit einem anderen Wert der Datengrundlage ‘Fläche’. Das Ziel ist, sicher zu gehen, dass Ihre Methodik für alle Daten in der Datengrundlage das selbe Ergebnis liefert.
Um nicht händisch jeden String im Folgenden überarbeiten zu müssen, sollten Sie nun versuchen einen Weg zu finden, wie Sie das automatisch bewerkstelligen können. Sprechen Sie sich in Ihrer Gruppe ab und erörtern und testen Sie potentielle Möglichkeiten - gern auch im sogenannten Peer-Programming.
Tipp
Schauen Sie sich das slicing noch mal genauer an. Kann man dies auch bei bestimmten Characters machen? Denken Sie daran, ein Leerzeichen ist auch ein Character - die Standardbibliothek könnte da weiterhelfen
Tipp 2
strip()
undfloat()
Wenn Sie nicht weiterkommen, treten Sie gedanklich ein Stück zurück oder malen Sie sich den Ablauf auf ein Stück Papier auf. Geben Sie sich dafür zusammen noch 10 Minuten zum Ausprobieren und Fragen Sie dann nach.
Wenn Sie eine Lösung gefunden haben versuchen Sie diese nun auf alle Elemente in der Liste anzuwenden. Gegebenenfalls hilft Ihnen eine Kombination aus for-Schleife für den Zugriff auf jedes Element der Liste und eine Funktion, die den String entsprechend bearbeitet und wieder zurückgibt.
Teil-Lösung - erst selber probieren!
Sie können zwei for-Schleifen Ansätze versuchen. Einmal einen indexbasierten Ansatz und einmal das direkte bearbeiten der Liste
flaeche_bereinigt = [] for i in range(0, len(flaeche)): # Extrahieren Sie das Element aus der Liste der Fläche an der entsprechenden Indexstelle # Rufen Sie ihre Funktion zum Bereinigen des Strings auf und übergeben Sie der Funktion das gerade extrahierte Element. # Initialisieren Sie eine neue Variable mit dem bereinigten String, welchen Ihre Funktion zurückgibt (return) # Fügen Sie der flaeche_bereinigt Liste den neuen, bereinigten String zu # oder: for element in flaeche: # Hier ist jedes *element* schon extrahiert. Ein Indexaufruf ist somit nicht nötig. # Übergeben Sie das Element dann Ihrer Funktion zum Bereinigen und initialisieren Sie eine neue Variable # Fügen Sie der flaeche_bereinigt-Liste den neuen, bereinigten String zu
Überprüfen Sie Ihr Ergebnis, indem Sie sich die neue Liste ausgeben lassen. Hat alles funktioniert?
Nun sind Sie dran! Bereinigen Sie die Liste der Einwohnerzahlen.
Schreiben Sie nun eine Funktion, um die Einwohnerdichte zu berechnen und testen Sie dies mit einzelnen Werten.
Wenden Sie nun beide Listen auf die eben geschriebene Funktion an und errechnen Sie für alle Werte die Einwohnerdichte. Speichern Sie diese in einer neuen Liste
einwohnerdichte
.
Bonusinhalt: Ausgewachsene Knobelei - für Daniel Düsentriebe
Nehmen Sie sich nun auch noch die Informationen von gestern bzgl. der Namen der Hauptstädte. Versuchen Sie, die folgenden Fragen zu beantworten:
Welche Stadt hat die größte Bevölkerung?
Welche die niedrigste Dichte?
Und welche die durchschnittlichste Größe (arithmetisches Mittel der flaeche)?
Kann Ihnen hier ein Dictionary helfen? Denken Sie daran, dass Sie Datentypen auch ‘verschachteln’ können. Sie können somit Listen in Listen oder Listen in Dictionaries geben.