Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
1 Einleitung
2 Die Programmiersprache Python
Teil I Einstieg in Python
3 Erste Schritte im interaktiven Modus
4 Der Weg zum ersten Programm
5 Kontrollstrukturen
6 Dateien
7 Das Laufzeitmodell
8 Funktionen, Methoden und Attribute
9 Informationsquellen zu Python
Teil II Datentypen
10 Das Nichts – NoneType
11 Operatoren
12 Numerische Datentypen
13 Sequenzielle Datentypen
14 Zuordnungen
15 Mengen
16 Collections
17 Datum und Zeit
18 Aufzählungstypen – Enum
Teil III Fortgeschrittene Programmiertechniken
19 Funktionen
20 Modularisierung
21 Objektorientierung
22 Ausnahmebehandlung
23 Iteratoren
24 Kontextobjekte
25 Manipulation von Funktionen und Methoden
Teil IV Die Standardbibliothek
26 Mathematik
27 Kryptografie
28 Reguläre Ausdrücke
29 Schnittstelle zu Betriebssystem und Laufzeitumgebung
30 Kommandozeilenparameter
31 Dateisystem
32 Parallele Programmierung
33 Datenspeicherung
34 Netzwerkkommunikation
35 Debugging und Qualitätssicherung
36 Dokumentation
Teil V Weiterführende Themen
37 Anbindung an andere Programmiersprachen
38 Distribution von Python-Projekten
39 Grafische Benutzeroberflächen
40 Python als serverseitige Programmiersprache im WWW – ein Einstieg in Django
41 Wissenschaftliches Rechnen
42 Insiderwissen
43 Von Python 2 nach Python 3
A Anhang
Stichwortverzeichnis

Download:
- Beispielprogramme, ca. 464 KB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Python 3 von Johannes Ernesti, Peter Kaiser
Das umfassende Handbuch
Buch: Python 3

Python 3
Pfeil 25 Manipulation von Funktionen und Methoden
Pfeil 25.1 Decorator
Pfeil 25.2 Das Modul functools
Pfeil 25.2.1 Funktionsschnittstellen vereinfachen
Pfeil 25.2.2 Methodenschnittstellen vereinfachen
Pfeil 25.2.3 Caches
Pfeil 25.2.4 Ordnungsrelationen vervollständigen
Pfeil 25.2.5 Überladen von Funktionen
 
Zum Seitenanfang

25.2    Das Modul functools Zur vorigen ÜberschriftZur nächsten Überschrift

Das Modul functools der Standardbibliothek enthält Funktionen und Decorators, mit deren Hilfe sich aufrufbare Objekte, beispielsweise Funktionen oder Methoden, auf einer abstrakten Ebene modifizieren lassen. In diesem Kapitel werden die Vereinfachung von Schnittstellen, das Hinzufügen eines Caches und das Vervollständigen der Ordnungsrelation besprochen.

[»]  Hinweis

Dieser Abschnitt richtet sich an fortgeschrittene Leser und setzt teilweise Wissen aus anderen Kapiteln voraus, insbesondere aus Kapitel 21, »Objektorientierung«.

 
Zum Seitenanfang

25.2.1    Funktionsschnittstellen vereinfachen Zur vorigen ÜberschriftZur nächsten Überschrift

Im Modul functools ist die Funktion partial enthalten, mit der sich Funktionsschnittstellen vereinfachen lassen. Betrachten Sie dazu die folgende Funktion:

def f(a, b, c, d):
print("{} {} {} {}".format(a,b,c,d))

Die Funktion f erwartet viele Parameter, vier an der Zahl. Stellen Sie sich nun vor, wir müssten die Funktion f sehr häufig im Programm aufrufen und übergäben dabei für die Parameter b, c und d immer die gleichen Werte. Mithilfe der Funktion partial lässt sich die Schnittstelle von f so verändern, dass nur der eigentlich interessante Parameter a übergeben werden muss.

partial(func, [*args], {**kwargs})

Die Funktion partial bekommt ein Funktionsobjekt übergeben, dessen Schnittstelle vereinfacht werden soll. Zusätzlich werden die zu fixierenden Positions- und Schlüsselwortparameter übergeben.

Die Funktion partial gibt ein neues Funktionsobjekt zurück, dessen Schnittstelle der von func entspricht, die jedoch um die in args (Arguments) und kwargs (Keyword Arguments) angegebenen Parameter erleichtert wurde. Bei einem Aufruf des zurückgegebenen Funktionsobjekts werden diese fixierten Parameter automatisch ergänzt.

Dies demonstriert das folgende Beispiel anhand der oben definierten Funktion f:

>>> def f(a, b, c, d):
... print("{} {} {} {}".format(a,b,c,d))
...
>>> import functools
>>> f_neu = functools.partial(f, b="du", c="schöne", d="Welt")
>>> f_neu("Hallo")
Hallo du schöne Welt
>>> f_neu("Tschüss")
Tschüss du schöne Welt

Zunächst wird die Funktion f definiert, die vier Parameter akzeptiert und diese hintereinander auf dem Bildschirm ausgibt. Da die letzten drei Parameter dieser Schnittstelle in unserem Programm immer gleich sind, möchten wir sie nicht immer wiederholen und vereinfachen die Schnittstelle mittels partial.

Dazu rufen wir die Funktion partial auf und übergeben das Funktionsobjekt von f als ersten Parameter. Danach folgen die drei feststehenden Werte für die Parameter b, c und d in Form von Schlüsselwortparametern. Die Funktion partial gibt ein Funktionsobjekt zurück, das der Funktion f mit vereinfachter Schnittstelle entspricht. Dieses Funktionsobjekt kann, wie im Beispiel zu sehen, mit einem einzigen Parameter aufgerufen werden. Der Funktion f wird dieser Parameter gemeinsam mit den drei fixierten Parametern übergeben.

Abgesehen von Schlüsselwortparametern können Sie der Funktion partial auch Positionsparameter übergeben, die dann ebenfalls als solche an die zu vereinfachende Funktion weitergegeben werden. Beachten Sie dabei, dass die feststehenden Parameter dann am Anfang der Funktionsschnittstelle stehen müssen. Dazu folgendes Beispiel:

>>> def f(a, b, c, d):
... print("{} {} {} {}".format(a,b,c,d))
...
>>> f_neu = functools.partial(f, "Hallo", "du", "schöne")
>>> f_neu("Welt")
Hallo du schöne Welt
>>> f_neu("Frau")
Hallo du schöne Frau

Die ersten drei Parameter der Funktion f sind immer gleich und sollen mithilfe der Funktion partial als Positionsparameter festgelegt werden. Das resultierende Funktionsobjekt f_neu kann mit einem Parameter aufgerufen werden, der beim daraus resultierenden Funktionsaufruf von f neben den drei festen Parametern als vierter übergeben wird.

 
Zum Seitenanfang

25.2.2    Methodenschnittstellen vereinfachen Zur vorigen ÜberschriftZur nächsten Überschrift

Analog zur soeben besprochenen Funktion partial zur Vereinfachung von Funktionsschnittstellen existiert die Funktion partialmethod zur Vereinfachung von Methodenschnittstellen. Mithilfe von partialmethod lassen sich Varianten einer Methode erzeugen, bei denen bestimmte Parameter vorbelegt sind. Dazu folgendes Beispiel:

>>> import functools
>>> class Zitat:
... def __init__(self):
... self.quelle = "Unbekannt"
... def zitat(self, text):
... print("{}: '{}'".format(self.quelle, text))
... def setze_quelle(self, quelle):
... self.quelle = quelle
... setze_donald = functools.partialmethod(setze_quelle, "Donald Duck")
... setze_goofy = functools.partialmethod(setze_quelle, "Goofy")
...
>>> zitat = Zitat()
>>> zitat.setze_donald()
>>> zitat.zitat("Quack")
Donald Duck: 'Quack'

Im Beispiel wurden zwei Vereinfachungen der Methode setze_quelle zur Klasse Zitat hinzugefügt, die jeweils einen bestimmten Autor festlegen. Die Funktion partialmethod verfügt über dieselbe Schnittstelle wie partial, und das Festlegen von Parametern funktioniert nach dem gleichen Prinzip.

 
Zum Seitenanfang

25.2.3    Caches Zur vorigen ÜberschriftZur nächsten Überschrift

Mithilfe des Decorators lru_cache, der im Modul functools enthalten ist, lässt sich eine Funktion mit einem Cache versehen. Ein Cache ist ein Speicher, der vergangene Funktionsaufrufe sichert. Wenn eine Parameterbelegung beim Funktionsaufruf bereits vorgekommen ist, kann das Ergebnis aus dem Cache gelesen werden, und die Funktion muss nicht noch einmal ausgeführt werden. Dieses Prinzip kann besonders bei rechenintensiven und häufig aufgerufenen Funktionen einen großen Laufzeitvorteil bringen.

[»]  Hinweis

Wenn ein Funktionsergebnis aus dem Cache gelesen wird, wird die Funktion nicht ausgeführt. Das Cachen ergibt also nur Sinn, wenn die Funktion frei von Seiteneffekten und deterministisch ist, also das Ergebnis bei der gleichen Parameterbelegung stets dasselbe ist.

lru_cache([maxsize, typed])

Der Decorator lru_cache versieht eine Funktion mit einem LRU Cache mit maxsize Einträgen. Bei einem LRU Cache (für Least Recently Used) verdrängt ein neuer Eintrag stets den am längsten nicht mehr aufgetretenen Eintrag, sofern der Cache vollständig gefüllt ist. Wenn für maxsize der Wert None übergeben wird, hat der Cache keine Maximalgröße und kann unbegrenzt wachsen. Der mit False vorbelegte Parameter typed gibt an, ob gleichwertige Instanzen verschiedener Datentypen, zum Beispiel 2 und 2.0, als gleich (False) oder als ungleich (True) angesehen werden sollen.

Im folgenden Beispiel wird die Funktion fak zur Berechnung der Fakultät einer ganzen Zahl definiert und mit einem Cache versehen:

>>> import functools
>>> @functools.lru_cache(20)
... def fak(n):
... res = 1
... for i in range(2, n+1):
... res *= i
... return res
...
>>> [fak(x) for x in [7, 5, 12, 3, 5, 7, 3]]
[5040, 120, 479001600, 6, 120, 5040, 6]

Mithilfe der Methode cache_info, die der Decorator lru_cache dem dekorierten Funktionsobjekt hinzufügt, erhalten Sie Informationen über den aktuellen Status des Caches:

>>> fak.cache_info()
CacheInfo(hits=3, misses=4, maxsize=20, currsize=4)

Das Ergebnis ist ein benanntes Tupel mit den folgenden Einträgen:

Eintrag Beschreibung
hits die Anzahl der Funktionsaufrufe, deren Ergebnisse aus dem Cache gelesen wurden
misses die Anzahl der Funktionsaufrufe, deren Ergebnisse nicht aus dem Cache gelesen wurden
maxsize die maximale Größe des Caches
currsize die aktuelle Größe des Caches

Tabelle 25.1    Einträge im CacheInfo-Tupel

Zusätzlich zu cache_info verfügt ein mit lru_cache dekoriertes Funktionsobjekt über die parameterlose Methode cache_clear, die den Cache leert.

[»]  Hinweis

Intern wird der Cache in Form eines Dictionarys realisiert, bei dem die Parameterbelegung eines Funktionsaufrufs als Schlüssel verwendet wird. Aus diesem Grund dürfen nur Instanzen von hashbaren Datentypen an ein Funktionsobjekt übergeben werden, das den hier vorgestellten LRU Cache verwendet.

 
Zum Seitenanfang

25.2.4    Ordnungsrelationen vervollständigen Zur vorigen ÜberschriftZur nächsten Überschrift

Eine Klasse, auf der eine Ordnungsrelation definiert sein soll, für die also die Vergleichsoperatoren <, <=, >, >= funktionieren sollen, muss jede der entsprechenden magischen Methoden __lt__, __le__, __gt__ und __ge__ implementieren, obwohl eine dieser Methoden bereits ausreichen würde, um die Ordnungsrelation zu beschreiben.

Der Decorator total_ordering, der im Modul functools enthalten ist, erweitert eine Klasse, die nur eine der oben genannten magischen Methoden und zusätzlich die Methode __eq__ bereitstellt, um die jeweils anderen Vergleichsmethoden. Das folgende Beispiel demonstriert die Verwendung des Decorators:

>>> import functools
>>> @functools.total_ordering
... class MeinString(str):
... def __eq__(self, other):
... return max(self) == max(other)
...
... def __lt__(self, other):
... return max(self) < max(other)
...
>>> MeinString("Hallo") > MeinString("Welt")
False
>>> MeinString("Hallo") <= MeinString("Welt")
True

Die Klasse MeinString erbt von dem eingebauten Datentyp str. Instanzen von MeinString sollen anhand des größten enthaltenen Buchstabens miteinander verglichen werden. Dazu sind die Methoden __eq__ für den Gleichheitsoperator und __lt__ für den Kleiner-Operator implementiert. Da die Klasse mit total_ordering dekoriert wurde, können auch die nicht explizit implementierten Vergleichsoperatoren verwendet werden.

 
Zum Seitenanfang

25.2.5    Überladen von Funktionen Zur vorigen ÜberschriftZur nächsten Überschrift

Es gibt Operationen, die für Instanzen verschiedener Datentypen definiert sind, aber je nach Datentyp unterschiedlich implementiert werden müssen. Ein Beispiel für solch eine Operation ist die eingebaute Funktion print, die anhand der übergebenen Datentypen eine Ausgabevariante auswählt.

Das Modul functools enthält den Decorator singledispatch, der das Überladen von Funktionen ermöglicht. Beim Überladen werden Implementierungsvarianten einer Funktion unter dem gleichen Namen hinzugefügt. Bei einem Aufruf der Funktion wählt der Interpreter anhand des Datentyps der übergebenen Parameter aus, welche konkrete Variante ausgeführt wird. Im Falle von singledispatch wird die auszuführende Variante anhand des Datentyps des ersten übergebenen Parameters ausgewählt, daher der Name.

Im folgenden Beispiel wird die Funktion mult definiert, die sich nicht um die ihr übergebenen Datentypen kümmert und daher ein unterschiedliches Verhalten für Zahlen und Strings aufweist:

>>> def mult(x):
... return x*2
...
>>> mult(5)
10
>>> mult("5")
'55'

Mithilfe des Decorators singledispatch lässt sich die Funktion für Strings überladen, sodass in diesem Fall eine Multiplikation auf dem im String enthaltenen Zahlenwert durchgeführt wird:

>>> import functools
>>> @functools.singledispatch
... def mult(x):
... return x*2
...
>>> @mult.register(str)
... def _(x):
... return str(int(x)*2)
...

Die Ausgangsfunktion mult wird wie im vorangegangenen Beispiel definiert und zusätzlich mit dem Decorator singledispatch versehen. Dieser Decorator erweitert sie um die Methode register, mithilfe derer sie sich überladen lässt.

Im zweiten Teil des Beispiels wird eine Variante der Methode mult für Strings implementiert. Diese Variante, die den temporären Namen »_« trägt, wird über den Decorator mult.register als Variante von mult registriert. Je nach Parameter wird jetzt eine der beiden Varianten von mult ausgeführt:

>>> mult(5)
10
>>> mult("5")
'10'

Auf diese Weise lässt sich eine Funktion beliebig oft überladen. Wenn eine Variante für mehrere Datentypen verfügbar sein soll, können die register-Decorator verkettet werden:

>>> @mult.register(float)
... @mult.register(str)
... def _(x):
... return str(int(x)*2)
...
>>> mult(5.0)
'10'
>>> mult("5")
'10'
[»]  Hinweis

Diese Art der Funktionsüberladung ist kein grundlegendes Konzept von Python, sondern wurde eingeführt, damit grundlegende Funktionen der Standardbibliothek, darunter print oder len, bequem in Python implementiert werden können. Die Funktionsüberladung in Python ist daher mit großen Einschränkungen verbunden:

  • Es können nicht beliebige Funktionen überladen werden, sondern nur solche, die mit singledispatch dekoriert wurden.
  • Eine überladbare Funktion darf nur über einen einzigen nicht-optionalen Parameter verfügen.
  • Andere Sprachen, beispielsweise C++, bieten umfassende Freiheiten beim Überladen von Funktionen. Davon müssen Sie diesen Ansatz unterscheiden.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Python 3 Python 3
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Einstieg in Python
Einstieg in Python


Zum Rheinwerk-Shop: Python. Der Grundkurs
Python. Der Grundkurs


Zum Rheinwerk-Shop: Algorithmen mit Python
Algorithmen mit Python


Zum Rheinwerk-Shop: Objektorientierte Programmierung
Objektorientierte Programmierung


Zum Rheinwerk-Shop: Raspberry Pi. Das umfassende Handbuch
Raspberry Pi. Das umfassende Handbuch


Zum Rheinwerk-Shop: Roboter-Autos mit dem Raspberry Pi
Roboter-Autos mit dem Raspberry Pi


Zum Rheinwerk-Shop: Neuronale Netze programmieren mit Python
Neuronale Netze programmieren mit Python


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2020
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern