.. include:: markup.rst ********** Funktionen ********** :mark:`Funktionen` sind Unterprogramme und helfen die * :mark:`Les- und Wartbarkeit` bei großen Programmen zu erleichtern (durch Unterteilung in Unterprogramme) sowie * :mark:`Redundanzen` zu vermeiden (wiederkehrende Algorithmen nur 1x implementiert). Diese * benötigen i.A. Eingaben - sog. :mark:`Parameter` (:mark:`Argumente`) - und * liefern (damit berechnete) Ergebnisse zurück, genannt :mark:`Rückgabewerte`. Wir haben bereits Funktionen kennengelernt, z.B. ``len`` :: In [1]: neue_liste = [3, 5, 9] In [2]: len(neue_liste) Out[2]: 3 ``neue_liste`` ist dabei ein Parameter, ``3`` der Rückgabewert. Den Rückgabewert kann man in eine Variable speichern und weiter verarbeiten mittels: :: In [3]: a = len(neue_liste) In [4]: a Out[4]: 3 In [5]: print("Laenge=", a) Laenge= 3 Bei den Sequenzen haben wir auch schon Funktionen von Klassen in Aktion gesehen: :: In [7]: s = "Zeichenkette" # erzeuge Zeichenkettenobjekt s In [8]: s.count('e') # zaehle Buchstaben 'e' Out[8]: 4 Diese werden mit Objektnamen (``s``) plus Punkt (``.``) plus Funktionsname (``count()``) aufgerufen. Genaueres dazu später. .. Ein :mark:`Hauptprogramm` (``main``-Funktion) verwaltet die Unterprogramme. Erste Schritte ============== Eine Funktion startet in Python mit dem :mark:`Schlüsselwort` ``def``. Syntaktisch sieht die Definition folgendermaßen aus: :: def Funktionsname( Parameterliste ) : Anweisung ... Anweisung return Ergebnis # Optional Beispiel: :: def fak(zahl): ergebnis = 1 for i in range(2, zahl+1): ergebnis *= i return ergebnis Dieser Funktion wird die Variable ``zahl`` als Parameter im :mark:`Anweisungskopf` übergeben. Der :mark:`Anweisungskörper` berechnet die Fakultät dieser Zahl und gibt das Ergebnis zurück. Speichert man diesen Quellcode in ein ``.py`` File und führt dieses aus, passiert nichts! Warum? * Die Funktion wurde zwar eingelesen, aber * :mark:`kein Befehl ruft diese Funktion` auf! Dieses Skript muss entsprechend erweitert werden, damit es die Funktion aufruft (``funktion_fak.py``): .. literalinclude:: src/funktion_fak.py Dieses File (z.B. ``funktion_fak.py``) kann man nun: * In Spyder laden und ausführen oder * in einer Python Shell mit ``python funktion_fak.py`` starten oder * in der IPython-Konsole ausführen mit ``runfile("funktion_fak.py")`` (Voraussetzung man befindet sich in diesem Verzeichnis) .. note:: * Die ``while`` Anweisung ist eine Endlosschleife, nicht gut, fürs Beispiel ok. * Die Kommunikation funktioniert über :mark:`Parameter` und ``return`` Anweisung. * Man erkennt: *Aufteilung*, keine *Redundanzen*, *Hauptprogramm* und *Funktion* * Die IPython-Konsole ist eine Linux Shell. siehe https://jakevdp.github.io/PythonDataScienceHandbook/01.05-ipython-and-shell-commands.html Z.B. ``ls`` (list) zeigt den Verzeichnisinhalt an, mit ``cd`` wechseln man Verzeichnisses) Funktionsparameter ================== Optionale und Schlüsselwortparameter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Parameter können * obligatorisch sowie * optional sein (mit ``=`` in Parameterliste). Die optionalen Parameter (0 oder mehr) folgen den obligatorischen. Zum Verständnis ein Beispiel (``funktion_add.py``): .. literalinclude:: src/funktion_add.py Aufrufe, hier einmal interaktiv: :: In [1]: runfile("funktion_add.py") In [2]: add(4) Out[2]: 9 In [3]: add(8,3) Out[3]: 11 .. note:: * ``runfile`` führt das file aus, dadurch wird die Funktion definiert und kann verwendet werden. * ``x`` ist obligatorisch. * ``y`` ist optional, wird diese Zahl nicht angegeben ist ``y=5`` (Default Wert). Es gibt unterschiedliche :mark:`Möglichkeiten des Aufrufes`. Zunächst eine Funktionsdefinition: Beispiel (``funktion_sumsub.py``): .. literalinclude:: src/funktion_sumsub.py Mögliche Aufrufe: :: In [1]: runfile("funktion_sumsub.py") In [2]: sumsub(12,4) #1 nur a und b übergeben, Rest = Defaultwerte Out[2]: 8 In [3]: sumsub(12,4,27,23) #2 Position optionaler Argumente fix Out[3]: 12 In [4]: sumsub(12,4,d=23,c=27) #3 Position optionaler Argumente beliebig! Out[4]: 12 Da die Position aller Argumente beim 1. und 2. Aufruf fix ist, nennt man diese :mark:`positional arguments`. Beim 3. Aufruf ist die Position optionaler Argumente beliebig. Die Zuordnung funktioniert über einen Schlüssel (``d=``, ``c=``). Daher nennt man diese auch :mark:`Schlüsselwortparameter` (:mark:`keyword arguments`) .. hint :: Der Aufruf ``#2`` ist zu vermeiden da fehleranfällig. Besser ist ``#3`` aber mit gleicher Reihenfolge wie bei der Funktionsdefinition ``c=27,d=23``! call-by-value / reference ~~~~~~~~~~~~~~~~~~~~~~~~~ Je nach Programmiersprache gibt es unterschiedliche Möglichkeiten wie Argumente übergeben werden: * :mark:`call-by-value`: Hier wird funktionsintern mit Kopien der als Parameter übergebenen Instanzen gearbeitet. **Vor- Nachteile**: Eine Funktion kann keine Änderungen von Instanzen aus dem Hauptprogramm bewirken, erzeugt jedoch unter Umständen einen erheblichen Overhead. * :mark:`call-by-reference`: Dabei wird funktionsintern mit Referenzen (Pointer) auf die im Hauptprogramm befindlichen Instanzen gearbeitet. **Vor- Nachteile**: "Schlanke" Calls da keine Daten erzeugt werden, jedoch besteht die Gefahr, dass eine Funktion Instanzen aus dem Hauptprogramm ändern kann. :mark:`Python` verwendet diesbezüglich eine :mark:`Mischform`. Eine :mark:`call-by-reference` gibt es nur bei veränderbaren Datentypen (``list``, ``dict``). Beispiel: :: In [1]: def test(liste): ...: liste += [5,6,7] ...: In [2]: zahlen = [1,2,3] In [3]: print(zahlen) [1, 2, 3] In [4]: test(zahlen) In [5]: print(zahlen) [1, 2, 3, 5, 6, 7] Man sieht die (ungewollte) :mark:`Veränderung der Liste` im Hauptprogramm. :mark:`Vermeidung` einfach durch :mark:`kopieren` beim Funktionsaufruf, d.h.: :: In [6]: zahlen = [1,2,3] In [7]: test(zahlen[:]) In [8]: print(zahlen) [1, 2, 3] Bei unveränderbaren Datentypen (z.B. Zahlen) wird ein :mark:`call-by-value` verwendet. Beispiel: :: In [1]: def test_zahl(zahl): ...: zahl += 2 ...: In [2]: a = 2 In [3]: print(a) 2 In [4]: test_zahl(a) In [5]: print(a) 2 Der Grund dafür, man hat in Python versucht die obigen :mark:`Vor- und Nachteile ideal zu kombinieren`. Docstring ~~~~~~~~~ Möchte man eine :mark:`Funktion dokumentieren`, schreibt man direkt nach dem Anweisungskopf einen Kommentar welcher mit ``"""`` (dreifaches Anführungszeichen) startet und endet. Den Text dazwischen nennt man :mark:`docstring`. Beispiel Funktion (``funktion_add.py``): .. literalinclude:: src/funktion_add.py Den :mark:`docstring` kann man folgendermaßen :mark:`ausgeben`: :: In [1]: runfile("funktion_add.py") In [2]: add.__doc__ Out[2]: 'Gib x plus y zurueck.' Die :mark:`magische Funktion` ``__doc__`` retourniert den :mark:`docstring`. Namensräume =========== Innerhalb eines Programmes gibt es einen Satz von :mark:`Variablen`. Diese sieht man in :mark:`Spyder` direkt. Diesen Variablen sind sogenannten :mark:`Namensräumen` zugewiesen in dem diese Werte definiert wurden. Es gibt eigene Namensräume (:mark:`namespaces`) z.B. innerhalb: * Hauptprogramm (global), * Funktionskörper (lokal), * Klassendefinitionen (lokal, siehe später). .. figure:: images/types_namespace-1.png :scale: 30 % :align: center Die Funktionen ``globals()`` und ``locals()`` zeigen den Inhalt dieser Namensräume. Beispiel: Zunächst definieren wir eine Funktion im File (``funktion_namespaces.py``): .. literalinclude:: src/funktion_namespaces.py und starten den interaktiven Modus: :: In [1]: globals() Out[1]: {'__name__': '__main__', ... '_i1': 'globals()'} In [2]: runfile("funktion_namespaces.py") In [3]: globals() Out[3]: {'__name__': '__main__', ... 'check': , '_i3': 'globals()'} Man sieht folgendes : * ``globals()`` gibt ein ``dict`` zurück. Es gibt schon :mark:`vordefinierte` Wertepaare. * Mit ``runfile`` wurde aus ``funktion_namespaces.py`` die Funkion ``check`` geladen (sowie viele andere Dinge). Dadurch kann diese Funktion erst aufgerufen werden mit: :: In [4]: check() {'x': 123} {'__name__': '__main__', ... 'check': } Man sieht folgendes : * Der Funktionskörper kennt (auch) ``globals()``, diese sind unverändert (d.h. ``check()`` kann sich selbst aufrufen) * Weiters kennt der Funktionskörper ``x`` (lokale Variable), * das Hauptprogramm kennt ``x`` aber nicht (keine globale Variable)! * ``...`` bedeutet es gibt wesentlich mehr Ausgabe in der Shell als hier dargestellt. Hier ein Beispiel :: In [1]: def test(): ...: x = 1 # lokale Variable ...: print(x) ...: In [2]: x=2 # globale Variable In [3]: test() # Ausgabe der lokalen Variable x 1 In [4]: x # Ausgabe der globalen Variable x Out[4]: 2 Abhilfe schafft eine ``global`` Anweisung. :: In [1]: def test(): ...: global x # x ist auch im globalen namespace sichtbar ...: x = 1 ...: print(x) ...: In [2]: x = 2 In [3]: test() # ueberschreibt x auch global 1 In [4]: x Out[4]: 1 .. warning:: Diese Art der Programmierung ist nicht gut, weil unübersichtlich und fehleranfällig! Ein typisches Beispiel sind globale Zähler z.B. bei Lizenzabfragen. Besser ist in diesem Fall ein ``return`` Statement: :: In [1]: def test(): ...: x = 1 ...: print(x) ...: return x ...: In [2]: x = 2 In [3]: x = test() # man sieht wo x ueberschrieben wird 1 In [4]: x Out[4]: 1 Vordefinierte Funktionen ======================== Python hat eine Reihe von sogenannten Built-in Functions. Hier eine (unvollständige) Liste: :: abs() bool() chr() complex() dict() enumerate() float() globals() help() list() input() int() id() len() pow() locals() max() min() open() range() repr() round() sorted() str() sum() tuple() type() Übungsbeispiele ================= weitere Übungsbeispiele =================