Eigene Funktionen und Module

Overview

Teaching: 60 min
Exercises: 0 min
Questions
  • Wie kann ich selbst eigene Funktionen definieren?

  • Wie können Funktionen aus externen Bibliotheken eingebunden werden?’

Objectives
  • Funktionalität von Programmen nachnutzen

Eigene Funktionen definieren

Bisher haben wir uns nur mit einfachen Anweisungen beschäftigt. Typischerweise wollen wir aber nicht immer wieder neu die gleichen Algorithmen an verschiedenen Stellen implementieren, sondern unser Programm soll eine komplexe Anweisung auf einmal ausführen. Dafür müssen wir unseren Code als eigene Funktion definieren:

def add_mult(a, b):
    output = a + b
    output = output + (a * b)
    return output

Eine Funktion besteht aus der Funktionsdefinition, die mit den Schlüssewort def eingeleitet und mit dem Doppelpunkt abgeschlossen wird. Darin steht der Name der Funktion (hier add_mult) und die Namen der Argumente, abgetrennt durch Komma. In dem Beispiel werden zwei Argumente a und b übergeben.

Der Funktionsdefinition folgt ein Code-Block, der die Funktion implementiert. Der Block ist hier mit der return Anweisung abgeschlossen, die die Funktion beendet und den Rückgabewert definiert.

Nachdem eine Funktion definiert ist, kann sie im Code aufgerufen werden, genauso wie die eingebauten Funktionen auch.

v = add_mult(1, 100)
print(v)

Übung

Schreiben Sie eine Funktion, die gegeben zweier Werte die Summe aller Zahlen zwischen dem ersten und dem zweiten Wert (inklusive) angibt. Rufen Sie diese Funktion für verschiedene Werte auf und geben Sie die Werte aus. Für z.B. die Argumente 5 und 13 soll die Funktion die Summe 5 + 6 + 7 + 8 + 9 + 10 + 12 + 13 = 81 berechnen.

Lösung

def complex_math(start, end):
    summe = 0
    for i in range(start, end+1):
        summe = summe + i
    return summe

print(complex_math(5,13))
print(complex_math(1,100))
print(complex_math(20,100))
print(complex_math(1,10))
81
5050
4860
55

Funktionen können Werte von verschiedenen Typen als Argument bekommen. Auch kann die gleiche Funktion verschiedene Typen von Werten zurückgeben und je nach Bedingungen verschiedene return Anweisungen ausführen (es wird aber immer nur die erste return Anweisung ausgeführt).

def fancy_add(val):
    if isinstance(val, str):
        return  val + " + 1"
    elif isinstance(val, int):
        return val + 1
    elif isinstance(val, float):
        return val + 1.0
    else:
        return 42

print(fancy_add(2))
print(fancy_add(2.0))
print(fancy_add("2"))
print(fancy_add(False))
3
3.0
2 + 1
1

Der Code nutzt die eingebaute Funktion isinstance um zu überprüfen, ob eine Variable einen gegebenen Typ hat.

Außerdem ist es möglich, dass eine Funktion mehr als einen Wert zurückgibt. In diesem Fall muss die return Anweisung alle Werte mit Komma getrennt auflisten. Die Zuweisung von mehreren Werten beim Aufruf einer Funktion erfolgt ebenfalls durch Auflistung mit Komma.

def get_str_info(text):
    l = len(text)
    c = text[0]
    return l, c

a, b = get_str_info("This is an arbitrary string, I swear")
36
T

Frage

Was bedeuten die Werte, die die Funktion zurückliefert?

In Python wird unterschieden zwischen positionalen Argumenten, die übergeben werden müssen und Keyword-Argumenten, die optional sind und für die ein Standard-Wert in der Funktions-Definitions angegeben werden kann, der genutzt wird wenn das Argument nicht übergeben wird. Hier ein Beispiel:

def print_log(log_text, with_exclamation_marks=False):
    if with_exclamation_marks:
        print('!!! '+log_text+' !!!')
    else:
        print(log_text)
    
print_log('Nothing happend.')
print_log('Alarm', with_exclamation_marks=True)
print_log('Alarm', True)
Nothing happend.
!!! Alarm !!!
!!! Alarm !!!

Der Standardwert für das Argument with_exclamation_marks ist hier False.

Module und der import Befehl

Eigene Module

Jede Python-Datei ist gleichzeitig eine sogenanntes Modul. Man z.B. kann man das folgende Python-Skript mit dem Namen poornlp.py abspeichern:

def get_str_info(text):
    l = len(text)
    c = text[0]
    return l, c

def is_noun(word):
    return word[0].isupper()

Dieses enthält jetzt die beiden Funktionen get_str_info und is_noun. Durch das Speichern in der Datei haben wir ein Modul gleichen Names (poornlp) erzeugt. Nun wollen wir diese Funktionen ja nachnutzen und nicht jedes mal in unsere Skripte kopieren. Zum Laden der Funktion in ein eigenes Skript oder in die interaktive Konsole kann man den import Befehl benutzen. Angenommen, das Skript mit dem Modul befindet sich im gleichen Ordner, dann kann man

import poornlp

aufrufen und bekommt Zugriff auf die beiden Funktionen über den Modulnamen, gefolgt von einem .:

poornlp.get_str_info("Das ist ein Text")

Möchte man eine bestimmte Funktion aus einem Modul importieren, geht das mit from ... import:

from poornlp import get_str_info

Danach ist es nicht mehr notwendig, für die importierte Funktion den Modulenamen anzugeben:

get_str_info("Das ist ein Text")

Wenn das Modul in einem Unterordner liegt, kann man ebenfalls durch import darauf zugreifen, muss aber wieder einen Punkt zwischen dem Elternmodul (dem Ordner) und dem Kindermodul angeben. Z.B. sei eine Ordnerstruktur mit einem Ordner mathmodules und zwei Python-Dateien in dem Ordner gegeben

mathmodules/
├── basic.py
└── fancy.py

basic.py:

def add(a,b):
    return a + b

fancy.py:

def add(val):
    if isinstance(val, str):
        return  val + " + 1"
    elif isinstance(val, int):
        return val + 1
    elif isinstance(val, float):
        return val + 1.0
    else:
        return 42

Der Import für basic sehe dann folgendermaßen aus:

import mathmodules.basic
mathmodules.basic.add(5,10)

Auch Untermodule müssen importiert werden, z.B. würde

mathmodules.fancy.add("A")

fehlschlagenn wenn nur mathmodules oder mathmodules.basic, aber nicht mathmodules.fancy importiert worden ist.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'mathmodules' has no attribute 'fancy'

Manchmal sind die Modulnamen sehr lang, dann kann man über import ... as ... abgekürzt werden

import mathmodules.basic as mb
mb.add(42,3.13)

Ähnliches geht auch, wenn man Funktionsnamen mit from ... import ... as ... importiert, was sogar eine Umbennung der Funktion erlaubt und hilft Namenskonflikte aufzulösen:

from mathmodules.fancy import add as fancy_add
fancy_add(42)

Standardbibliotheken und Installation von neuen Bibliotheken

Python besitzt neben den eingebauten Funktionen und Typen auch noch eine riesige sogenannte Standardbibliothek. Dies sind Module, die in allen Python-Installationen (für eine bestimmte Version von Python) immer vorhanden sind. Sie können also erwarten, dass Sie in jedem Skript diese Module importieren können.

Eine Auflistung und Dokumentation der Module der Standardbibliothek findet sich unter https://docs.python.org/3.7/library/index.html.

Reguläre Ausdrücke auf Strings mit Modul re

Ein Beispiel für eine sehr nützliches Module aus der Standardbibliothek ist das Modul für reguläre Ausdrücke (also Mustersuche) mit dem Namen re. Muster oder „Patterns“ müssen erst einmal erstellt („kompilliert“) werden und kann dann auf Strings angewendet werden.

import re
pattern = re.compile('Glü.+')
# gibt nicht None zurück, wenn der ganze String dem Pattern matched
is_match = pattern.match("Was für ein Glück")   
# gibt nicht None zurück, wenn ein Teilstring dem Pattern matched
found = pattern.search("Was für ein Glück")

# Überprüfe ob die jeweiligen Ergebnisse nicht None sind
if is_match:
    print('It is a full match!')
elif found:
    print('I found it somewhere!')
    print("Found string =", found.group(0))
I found it somewhere!
Found string = Glück

Auf der Dokumentation des Moduls re gibt es eine Einführung in die Syntax dieser Patterns und wie man nicht nur ja/nein Suchen sondern auch die Position des Treffers bekommen kann.

Installation neuer Pakete mit pip

Hilfreiche allgemeine Python-Pakete, können über den „Python Package Index“ unter https://pypi.org gefunden werden. Die Installation auf dem lokalen System erfolgt dann mit dem Kommandozeilentool pip.

Um pip nutzen zu können, müssen Sie es unter Umständen erst in Conda installieren. Führen Sie dazu in Ihrer System-Kommandozeile mit aktiviertem Conda (z.B. im VS Code Terminal), folgenden Befehl aus:

conda install pip

Wenn Sie die Installation bestätigen, können Sie danach mit pip list eine Liste aller bereits installierte Pakete aufrufen.

pip list
Package                Version   
---------------------- ----------
asn1crypto             1.2.0     
astroid                2.3.3     
autopep8               1.5.1     
certifi                2020.4.5.1
cffi                   1.13.0    
chardet                3.0.4     
conda                  4.8.3     
conda-package-handling 1.6.0     
cryptography           2.8       
idna                   2.8       
isort                  4.3.21    
lazy-object-proxy      1.4.3     
mccabe                 0.6.1     
pathtools              0.1.2     
pip                    20.0.2    
pycodestyle            2.5.0     
pycosat                0.6.3     
pycparser              2.19      
pylint                 2.4.4     
pyOpenSSL              19.0.0    
PySocks                1.7.1     
requests               2.22.0    
ruamel-yaml            0.15.46   
setuptools             41.4.0    
six                    1.12.0    
tqdm                   4.36.1    
treetaggerwrapper      2.3       
typed-ast              1.4.1     
urllib3                1.24.2    
watchdog               0.10.2    
wheel                  0.33.6    
wrapt                  1.11.2  

Conda vs. pip

Conda ist ähnlich wie pip auch ein Paketmanager, kann also zum Beispiel mit conda install Pakete nachinstallieren. Wir haben Conda benutzt um ein Basissystem mit Python 3.7 und pip zu installieren, im Folgenden werden wir aber ausschließlich pip benutzen. Welchen der Paketmanager Sie für Ihre eigenen Projekte nutzen wollen ist Geschmacksfrage. Conda kann teilweise mit Paketen, die Nicht-Python-Abhängigkeiten (wie zum Beispiel Compiler oder Bibliotheken für maschinelles Lernen) nutzen besser verwalten, es ist mehr vergleichbar mit einem Paketmanager für Betriebsysteme. Im Gegensatz dazu, ist der Fokus von pip auf reinen Python-Abhängigkeiten. Der Python Package Index (PyPI) als zentrale Anlaufstelle für pip-Pakete stellt enorm viele Pakete bereit und es ist auch einfach möglich, selbst Python-Pakete auf PyPI zu veröffentlichen. Neuere Versionen von Conda sind in der Lage, beim Paketmanagement mit pip zu kooperieren, conda list wird zum Beispiel auch die mit pip installiereten Pakete aufzeigen.

Wir wollen nun auf PyPI ein Paket finden, mit dem wir sogenanntes ASCII Art generieren wollen. Dazu benutzen wir auf https://pypi.org/ die Suche, z.B. nach „ascii art“. Nach etwas stöbern in der Trefferliste, finden wir zum Beispiel das Paket art: https://pypi.org/project/art/

Sie sollten sich immer genaue die Metadaten zum Paket, wie z.B. die Lizenz und Autorenschaft anschauen. PyPI Metadaten für das Projekt art

Mit dem Befehl

pip install art

in der System-Kommandozeile kann das Paket installiert werden.

Danach ist es über import art für eigene Python-Skripte oder auf der interaktiven Python-Konsole verfügbar:

import art

print(art.text2art("Python"))
 ____          _    _                   
|  _ \  _   _ | |_ | |__    ___   _ __  
| |_) || | | || __|| '_ \  / _ \ | '_ \ 
|  __/ | |_| || |_ | | | || (_) || | | |
|_|     \__, | \__||_| |_| \___/ |_| |_|
        |___/                           

Kernpunkte

  • Mit Funktionen können Teile von Programmen ausgegliedert und nachgenutzt werden.

  • Eigene Module können in Python-Dateien abgespeichert und mit import in anderen Python-Skripten nachgenutzt werden.

  • Mit pip können neue Pakete, die Module enthalten, installiert werden.