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 21 Objektorientierung
Pfeil 21.1 Klassen
Pfeil 21.1.1 Definieren von Methoden
Pfeil 21.1.2 Der Konstruktor und die Erzeugung von Attributen
Pfeil 21.2 Vererbung
Pfeil 21.2.1 Technische Grundlagen
Pfeil 21.2.2 Die Klasse GirokontoMitTagesumsatz
Pfeil 21.2.3 Mögliche Erweiterungen der Klasse Konto
Pfeil 21.2.4 Ausblick
Pfeil 21.2.5 Mehrfachvererbung
Pfeil 21.3 Setter und Getter und Property Attributes
Pfeil 21.3.1 Setter und Getter
Pfeil 21.3.2 Property-Attribute
Pfeil 21.4 Klassenattribute und Klassenmethoden sowie statische Methoden
Pfeil 21.4.1 Statische Methoden
Pfeil 21.4.2 Klassenmethoden
Pfeil 21.4.3 Klassenattribute
Pfeil 21.5 Built-in Functions für Objektorientierung
Pfeil 21.5.1 Funktionen für die Verwaltung der Attribute einer Instanz
Pfeil 21.5.2 Funktionen für Informationen über die Klassenhierarchie
Pfeil 21.6 Objektphilosophie
Pfeil 21.7 Magic Methods und Magic Attributes
Pfeil 21.7.1 Allgemeine Magic Methods
Pfeil 21.7.2 Operatoren überladen
Pfeil 21.7.3 Datentypen emulieren
 
Zum Seitenanfang

21.7    Magic Methods und Magic Attributes Zur vorigen ÜberschriftZur nächsten Überschrift

Es gibt in Python eine Reihe spezieller Methoden und Attribute, um Klassen besondere Fähigkeiten zu geben. Die Namen dieser Methoden und Attribute beginnen und enden jeweils mit zwei Unterstrichen. Im Laufe der letzten Abschnitte haben Sie bereits eine dieser sogenannten Magic Methods bzw. Magic Attributes kennengelernt, nämlich den Konstruktor namens __init__.

Der Umgang mit diesen Methoden und Attributen ist insofern »magisch«, als dass sie in der Regel nicht direkt mit ihrem Namen benutzt, sondern bei Bedarf implizit im Hintergrund verwendet werden. Der Konstruktor __init__ wird beispielsweise immer dann aufgerufen, wenn ein neues Objekt einer Klasse erzeugt wird, auch wenn kein expliziter Aufruf zum Beispiel mit Klassenname.__init__() an der entsprechenden Stelle steht.

Mit vielen Magic Methods lässt sich das Verhalten von Built-in Functions und Operatoren für die eigenen Klassen anpassen, sodass die Instanzen Ihrer Klassen beispielsweise sinnvoll mit den Vergleichsoperatoren < und > verglichen werden können.

Tabelle 21.3 enthält häufig genutzte Magic Methods.

 
Zum Seitenanfang

21.7.1    Allgemeine Magic Methods Zur vorigen ÜberschriftZur nächsten Überschrift

Name Beschreibung
__init__(self, ...) Der Konstruktor einer Klasse. Wird beim Erzeugen einer neuen Instanz aufgerufen. Näheres dazu erfahren Sie in Abschnitt 21.2.1, »Technische Grundlagen«.
__del__(self) Der Finalizer einer Klasse. Wird beim Zerstören einer Instanz aufgerufen.
__repr__(self) Der Rückgabewert von obj.__repr__ gibt an, was repr(obj) zurückgeben soll. Dies sollte nach Möglichkeit gültiger Python-Code sein, der beim Ausführen die Instanz obj erzeugt.
__str__(self) Der Rückgabewert von obj.__str__ gibt an, was str(obj) zurückgeben soll. Dies sollte nach Möglichkeit eine für den Menschen lesbare Repräsentation von obj in Form einer str-Instanz sein.
__bytes__(self) Der Rückgabewert von obj.__bytes__ gibt an, was bytes(obj) zurückgeben soll. Dies sollte eine bytes-Instanz sein.
__bool__(self) Die __bool__-Methode sollte einen Wahrheitswert zurückgeben, der angibt, wie das Objekt in eine bool-Instanz umzuwandeln ist.
Ist __bool__ nicht implementiert, wird stattdessen der Rückgabewert von __len__ verwendet. Sind beide Methoden nicht vorhanden, werden alle Instanzen der betreffenden Klasse als True behandelt.
__call__(self, ...) Mit der __call__-Methode werden die Instanzen einer Klasse wie Funktionen aufrufbar.
__complex__(self) Legt fest, welchen Wert die Built-in Function complex für eine Instanz der Klasse zurückgeben soll.
__int__(self) Legt fest, welchen Wert die Built-in Function int für eine Instanz der Klasse zurückgeben soll.
__float__(self) Legt fest, welchen Wert die Built-in Function float für eine Instanz der Klasse zurückgeben soll.
__round__(self, [n]) Legt fest, welchen Wert die Built-in Function round für eine Instanz der Klasse zurückgeben soll. Der Parameter n gibt dabei an, auf wie viele Nachkommastellen gerundet werden soll.
__hash__(self) Die __hash__-Methode einer Instanz bestimmt, welchen Wert die Built-in Function hash für die Instanz zurückgeben soll.
__index__(self) Wenn ein Datentyp als Index benutzt werden soll, wie er beispielsweise für das Slicing benötigt wird, muss er die parameterlose Methode __index__(self) überschreiben. Der Rückgabewert von __index__ muss eine Ganzzahl (int) sein.

Tabelle 21.3    Allgemeine Magic Methods

Nun werden einige der Methoden im Detail besprochen.

__del__(self)

Der Finalizer __del__ (self)einer Instanz wird dann gerufen, wenn keine Referenz mehr auf die Instanz zeigt und sie von Pythons Speicherverwaltung zerstört wird. Im folgenden Beispiel wird der Finalizer der Klasse A daher nur einmal gerufen:

>>> class A:
... def __del__(self):
... print("Hier spricht der Destruktor.")
>>> a = A()
>>> b = a
>>> del a
>>> del b
Hier spricht der Destruktor.

Die Anweisung del x ruft also nicht sofort x.__del__.

[»]  Hinweis

Es wird nicht garantiert, dass der Finalizer innerhalb einer bestimmten Zeit gerufen wird, nachdem die letzte Referenz auf eine Instanz gelöscht wurde. Er eignet sich daher nur bedingt für Aufräumarbeiten wie das Schließen von Dateien oder das Beenden von Netzwerkverbindungen.

Im folgenden Beispiel sehen Sie zwei Instanzen a und b, die sich über das Attribut zyklisch referenzieren. Dadurch gibt es auch dann noch Referenzen auf die beiden Instanzen, wenn sie für das restliche Programm nicht mehr erreichbar sind.

>>> class B:
... def __init__(self, name):
... self.Name = name
... def __del__(self):
... print("Hier spricht der Destruktor von", self.Name)
>>> a = B("a")
>>> b = B("b")
>>> a.X = b
>>> b.X = a
>>> del a,b
>>>

Interessanterweise wird an dieser Stelle kein Finalizer der nicht mehr erreichbaren Instanzen gerufen. Erst beim Beenden des Python-Interpreters erfolgen die Aufrufe der __del__-Methode:

>>> exit()
Hier spricht der Destruktor von a
Hier spricht der Destruktor von b

Sie können sich also nicht darauf verlassen, dass der Finalizer zeitnah nach dem Löschen der letzten zugänglichen Referenz auf eine Instanz gerufen wird.

[»]  Hinweis

Da sich Pythons Speicherverwaltung um die Freigabe von Speicher kümmert, ist der Finalizer von geringerer Bedeutung als der Destruktor in anderen Sprachen mit manueller Speicherverwaltung wie etwa C++.

Insbesondere sollten Sie sich dessen bewusst sein, dass Python den Finalizer nicht unmittelbar nach dem Löschen der letzten Referenz auf eine Instanz aufruft, sondern zu irgendeinem nicht festgelegten Zeitpunkt.

__call__(self, …)

Mit der __call__-Methode werden die Instanzen einer Klasse wie Funktionen aufrufbar.

Das folgende Beispiel implementiert eine Klasse Potenz, die dazu dient, Potenzen zu berechnen. Welcher Exponent dabei verwendet werden soll, wird dem Konstruktor als Parameter übergeben. Durch die __call__-Methode können die Instanzen von Potenz wie Funktionen aufgerufen werden, um Potenzen zu berechnen:

class Potenz:
def __init__(self, exponent):
self.Exponent = exponent

def __call__(self, basis):
return basis ** self.Exponent

Nun können wir bequem mit Potenzen arbeiten:

>>> hoch3 = Potenz(3)
>>> hoch3(2)
8
>>> hoch3(5)
125

__hash__(self)

Die __hash__-Methode einer Instanz bestimmt, welchen Wert die Built-in Function hash für die Instanz zurückgeben soll. Die Hash-Werte müssen Ganzzahlen sein und sind insbesondere für die Verwendung von Instanzen als Schlüssel für Dictionarys von Bedeutung.

Bedingung für einen gültigen Hash-Wert ist, dass Objekte, die bei Vergleichen mit == als gleich angesehen werden, auch den gleichen Hash-Wert besitzen. Außerdem darf sich der Hash-Wert einer Instanz nicht zur Laufzeit ändern, weshalb er nur für immutable Datentypen sinnvoll definiert werden kann.

[»]  Hinweis

Eine Klasse, die __hash__ implementiert, sollte zusätzlich die Methode __eq__ implementieren. Das macht sie hashable. Es können nur hashable Instanzen als Schlüssel für ein Dictionary verwendet oder in Mengen gespeichert werden.

Zugriff auf Attribute anpassen

Die Methoden und Attribute in diesem Abschnitt dienen dazu, festzulegen, wie Python vorgehen soll, wenn die Attribute einer Instanz gelesen oder geschrieben werden.

Name Beschreibung
__dict__ Jede Instanz besitzt ein Attribut namens __dict__, das die Member der Instanz in einem Dictionary speichert.
__getattr__(self, name) Wird dann aufgerufen, wenn das Attribut mit dem Namen name gelesen wird, aber nicht existiert.
Die Methode __getattr__ sollte entweder einen Wert zurückgeben, der für das Attribut gelten soll, oder einen AttributeError erzeugen.
__getattribute__(self, name) Wird immer aufgerufen, wenn der Wert des Attributs mit dem Namen name gelesen wird, auch wenn das Attribut bereits existiert.
__setattr__(self, name, value) Die Methode __setattr__ wird immer dann aufgerufen, wenn der Wert eines Attributs per Zuweisung geändert oder ein neues Attribut erzeugt wird.
__delattr__(self, name) Wird aufgerufen, wenn das Attribut mit dem Namen name per del gelöscht wird.
__slots__ Weist Python an, die Attribute einer Klasse speicherschonend zu verwalten.

Tabelle 21.4    Methoden und Attribute, um den Zugriff auf Attribute zu regeln

Für einige der Tabelleneinträge folgt eine ausführlichere Besprechung.

__dict__

Jede Instanz besitzt ein Attribut namens __dict__, das die Member der Instanz in einem Dictionary speichert.

Die beiden folgenden Code-Zeilen produzieren also das gleiche Ergebnis, vorausgesetzt, obj ist eine Instanz einer Klasse, die ein Attribut A definiert:

>>> obj.A
"Der Wert des Attributs A"
>>> obj.__dict__["A"]
"Der Wert des Attributs A"

__getattribute__ (self, name)

Wird immer aufgerufen, wenn der Wert des Attributs mit dem Namen name gelesen wird, auch wenn das Attribut bereits existiert.

Implementiert eine Klasse sowohl __getattr__ als auch __getattribute__(self, name), wird nur letztere Funktion beim Lesen von Attributen aufgerufen, es sei denn, __getattribute__ ruft selbst __getattr__ auf.

[»]  Hinweis

Greifen Sie innerhalb von __getattribute__ niemals mit self.attribut auf die Attribute der Instanz zu, weil dies eine endlose Rekursion zur Folge hätte.

Benutzen Sie stattdessen immer __getattribute__ der Basisklasse, zum Beispiel object.__getattribute__(self, "attribut").

__setattr__(self, name, value)

Die Methode __setattr__ wird immer dann aufgerufen, wenn der Wert eines Attributs per Zuweisung geändert oder ein neues Attribut erzeugt wird. Der Parameter name gibt dabei einen String an, der den Namen des zu verändernden Attributs enthält. Mit value wird der neue Wert übergeben.

Mit __setattr__ lässt sich zum Beispiel festlegen, welche Attribute eine Instanz überhaupt haben darf, indem alle anderen Werte einfach ignoriert oder mit Fehlerausgaben quittiert werden.

[»]  Hinweis

Verwenden Sie innerhalb von __setattr__ niemals eine Zuweisung der Form self.attribut = wert, um die Attribute auf bestimmte Werte zu setzen, da dies eine endlose Rekursion bewirken würde: Bei jeder Zuweisung würde __setattr__ erneut aufgerufen.

Um Attributwerte in __setattr__ zu verändern, verwenden Sie die __setattr__-Methode der Basisklassen, zum Beispiel object.__setattr__(self, "attribut", wert).

__slots__

Instanzen in Python sind flexibel und mächtig, was die Arbeit mit Python angenehm macht. Beispielsweise können Sie zur Laufzeit Attribute dynamisch hinzufügen.

>>> class A:
... pass
>>> a = A()
>>> a.X = 10
>>> a.X
10

Diese Flexibilität wird durch Rechenzeit und Speicher erkauft, da für jede Instanz eine dict-Instanz erzeugt wird, um die Attribute zu verwalten.

Wenn Sie eine einfache Klasse mit wenigen Attributen definieren, von der es zur Laufzeit eine sehr große Anzahl von Instanzen gibt, kann dies unnötig Speicher vergeuden.

Um in einem solchen Fall Speicher zu sparen, können Sie die Attribute der Instanzen einer Klasse bei der Klassendefinition einschränken. Dadurch geht zwar die Flexibilität verloren, dynamisch neue Attribute anlegen zu können, aber der Python-Interpreter kann die Attribute dann effizienter verwalten, sodass Speicher eingespart wird.

Im folgenden Beispiel wird eine Klasse B definiert, deren Instanzen nur die Attribute X und Y haben können.

>>> class B:
... __slots__ = ("X", "Y")
... def __init__(self):
... self.X = 1
... self.Y = 2
>>> b = B()
>>> b.X
1
>>> b.Y
2
>>> b.Z = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'B' object has no attribute 'Z'

Wie Sie sehen, ist es nicht möglich, ein weiteres Attribut Z anzulegen. Dafür verbrauchen Instanzen der Klasse B weniger Speicher als die einer Klasse ohne __slots__-Definition.

[»]  Hinweis

Es gibt einige Besonderheiten, die den Umgang mit __slots__ betreffen. Beispielsweise lässt sich eine __slots__-Definition nicht auf Subklassen vererben.

 
Zum Seitenanfang

21.7.2    Operatoren überladen Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Operator ist eine Vorschrift, die aus einer Reihe von Operanden einen neuen Wert berechnet. Ihnen sind in diesem Buch schon häufiger Operatoren begegnet, beispielsweise in Form von Rechenzeichen.

>>> 1 + 2
3

In diesem Beispiel wurde der Operator + verwendet, um die Summe zweier int-Instanzen zu berechnen. Der Operator + kann aber auch verwendet werden, um beispielsweise Strings miteinander zu verketten.

>>> "Hallo " + "Welt"
'Hallo Welt'

Diese Mehrfachbelegung eines Operators wird dadurch ermöglicht, dass intern eine spezielle Methode gerufen wird, die festlegt, was der Operator bewirken soll. Im Falle des Operators + ist dies die Methode __add__. Die beiden folgenden Ausdrücke sind daher gleichwertig.[ 95 ](Die Klammern um die 1 sind deshalb notwendig, weil ein Punkt direkt hinter der 1 als Dezimalpunkt interpretiert werden würde. )

>>> 1 + 2
3
>>> (1).__add__(2)
3

Sie können also auch für Ihre eigenen Klassen Operatoren definieren, indem Sie die dahinterstehenden Methoden überschreiben.

Als Beispiel werden wir eine kleine Klasse zum Verwalten von Längenangaben mit Einheiten implementieren, die die Operatoren für Addition und Subtraktion unterstützt. Dabei dient die Methode __sub__ zur Implementation des Operators .

Die Klasse wird intern alle Maße für die Berechnungen in Meter umwandeln. Ihre Definition sieht dann folgendermaßen aus:

class Laenge:
Umrechnung = {"m" : 1, "dm" : 0.1, "cm" : 0.01,
"mm" : 0.001, "km" : 1000,
"ft" : 0.3048, # Fuß
"in" : 0.0254, # Zoll
"mi" : 1609344 # Meilen
}
def __init__(self, zahlenwert, einheit):
self.Zahlenwert = zahlenwert
self.Einheit = einheit
def __str__(self):
return "{:f} {}".format(self.Zahlenwert, self.Einheit)
def __add__(self, other):
z = self.Zahlenwert * Laenge.Umrechnung[self.Einheit]
z += other.Zahlenwert * Laenge.Umrechnung[other.Einheit]
z /= Laenge.Umrechnung[self.Einheit]
return Laenge(z, self.Einheit)
def __sub__(self, other):
z = self.Zahlenwert * Laenge.Umrechnung[self.Einheit]
z -= other.Zahlenwert * Laenge.Umrechnung[other.Einheit]
z /= Laenge.Umrechnung[self.Einheit]
return Laenge(z, self.Einheit)

Das Dictionary Laenge.Umrechnung enthält Faktoren, mit denen geläufige Längenmaße in Meter umgerechnet werden. Die Methoden __add__ und __sub__ überladen jeweils den Operator für Addition + bzw. den für Subtraktion -, indem sie zuerst die Zahlenwerte beider Operanden gemäß ihren Einheiten in Meter umwandeln, verrechnen und schließlich wieder in die Einheit des weiter links stehenden Operanden konvertieren.

Betrachten wir einmal folgende Anwendung der Klasse Laenge:

>>> a1 = Laenge(5, "cm")
>>> a2 = Laenge(3, "dm")
>>> print(a1 + a2)
35.000000 cm
>>> print(a2 + a1)
3.500000 dm

Wie Sie sehen, funktionieren die Rechnungen wie gewünscht. Bemerkenswert ist, dass sich die Einheit in der Ausgabe je nach Operandenreihenfolge verändert. Dies resultiert daraus, dass unsere Klasse Laenge immer die Einheit des weiter links stehenden Operanden als Einheit des Ergebnisses verwendet.

Neben den Operatoren + und - gibt es in Python eine Reihe weiterer Operatoren. Dabei unterscheiden wir mehrere Typen von Operatoren, wie es Tabelle 21.5 zeigt.

Kategorie Beschreibung Beispiele
Vergleichsoperatoren Vergleichen zwei Instanzen miteinander und liefern eine bool-Instanz als Ergebnis. <, >, =
binäre arithmetische Operatoren Operatoren, die auf zwei Operanden angewendet werden. Der Rückgabetyp hängt von dem Operator und den Operanden ab. +, -, *, /, %, @
binäre Operatoren mit umgekehrter Operandenreihenfolge Operatoren, die auf zwei Operanden angewendet werden. Der Rückgabetyp hängt von dem Operator und den Operanden ab. +, -, *, /, %, @
erweiterte Zuweisungen Operatoren, die eine Operation und eine Zuweisung verbinden +=, -=, *=, /=, @=
unäre Operatoren Operatoren mit nur einem Operanden, wie beispielsweise Vorzeichen +, -

Tabelle 21.5    Arten von Operatoren

Der Operator @ ist mit Python 3.5 neu hinzugekommen und dient dazu, eine gut lesbare Syntax für Matrix-Vektor-Multiplikationen anzubieten.

Vergleichsoperatoren

Die folgenden Magic Methods dienen dazu, das Verhalten der Vergleichsoperatoren für die Klasse anzupassen.

Um beispielsweise zwei Instanzen der Klasse Konto (siehe Abschnitt 21.1.2) zu vergleichen, kann die Kontonummer herangezogen werden. Damit gibt es eine sinnvolle Interpretation für den Vergleich mit == bei Konten. Die Methode für Vergleiche mit == heißt __eq__ (von engl. equals »ist gleich«) und erwartet als Parameter eine Instanz, mit der das Objekt verglichen werden soll, für das __eq__ aufgerufen wurde.

Der folgende Beispiel-Code erweitert unsere Konto-Klasse aus der Einführung zur Objektorientierung um die Fähigkeit, sinnvoll mit == verglichen zu werden:

class Konto:
def __init__(self, inhaber, kontonummer, kontostand,
max_tagesumsatz=1500):
self.Inhaber = inhaber
self.Kontonummer = kontonummer
self.Kontostand = kontostand
self.MaxTagesumsatz = max_tagesumsatz
self.UmsatzHeute = 0

def __eq__(self, k2):
return self.Kontonummer == k2.Kontonummer

Nun erzeugen wir drei Konten, wobei zwei die gleiche Kontonummer haben, und vergleichen sie mit dem ==-Operator. Das Szenario wird natürlich immer ein Wunschtraum für Donald Duck bleiben:

>>> konto1 = Konto("Dagobert Duck", 1337, 9999999999999999)
>>> konto2 = Konto("Donald Duck", 1337, 1.5)
>>> konto3 = Konto("Gustav Gans", 2674, 50000)
>>> konto1 == konto2
True
>>> konto1 == konto3
False

Die Anweisung konto1 == konto2 wird intern von Python beim Ausführen durch konto1.__eq__(konto2) ersetzt.

Neben der __eq__-Methode gibt es eine Reihe weiterer Vergleichsmethoden, die jeweils einem Vergleichsoperator entsprechen. Alle diese Methoden erwarten neben self einen weiteren Parameter, der die Instanz referenzieren muss, mit der self verglichen werden soll.

Tabelle 21.6 zeigt alle Vergleichsmethoden mit ihren Entsprechungen. Die Herkunftsspalte kann Ihnen helfen, sich die Methodennamen und ihre Bedeutung besser zu merken.

Operator Methode Herkunft
< __lt__(self, other) less than (dt. »kleiner als«)
<= __le__(self, other) less or equal (dt. »kleiner oder gleich«)
== __eq__(self, other) equal (dt. »gleich«)
!= __ne__(self, other) not equal (dt. »ungleich«)
> __gt__(self, other) greater than (dt. »größer als«)
>= __ge__(self, other) greater or equal (dt. »größer oder gleich«)

Tabelle 21.6    Die Magic Methods für Vergleiche

[»]  Hinweis

Wenn eine Klasse keine der Methoden __eq__ oder __ne__ implementiert, werden Instanzen der Klasse mittels == und != anhand ihrer Identität miteinander verglichen.

Ist es nicht möglich, die von self referenzierte Instanz mit other zu vergleichen, sollte NotImplemented zurückgegeben werden.

Binäre Operatoren

Ein binärer Operator ist ein Operator, der zwei Operanden verarbeitet. Beispiele für binäre Operatoren sind +, -, * und /.

Alle Methoden zum Überladen binärer Operatoren erwarten einen Parameter, der den zweiten Operanden referenziert. Als erster Operator wird immer diejenige Instanz verwendet, die für den Parameter self übergeben wurde. Ihr Rückgabewert muss eine neue Instanz sein, die das Ergebnis der Rechnung enthält.

Ein Beispiel für die Verwendung binärer Operatoren finden Sie zu Beginn dieses Abschnitts.

In Tabelle 21.7 sind alle binären Operatoren[ 96 ](Selbstverständlich sind die Vergleichsoperatoren auch binäre Operatoren. Aus Gründen der Übersicht haben wir sie aber separat besprochen. ) und die entsprechenden Magic Methods aufgelistet:

Operator Magic Method Operator Magic Method
+ __add__(self, other) % __mod__(self, other)
- __sub__(self, other) >> __lshift__(self, other)
* __mul__(self, other) << __rshift__(self, other)
/ __truediv__(self, other) & __and__(self, other)
// __floordiv__(self, other) | __or__(self, other)
divmod() __divmod__(self, other) ^ __xor__(self, other)
** __pow__(self, other, [modulo])

Tabelle 21.7    Magic Methods für binäre Operatoren

Binäre arithmetische Operatoren mit umgekehrter Operandenreihenfolge

Wenn Python einen Ausdruck der Form Operand1 Operator Operand2 wie beispielsweise 2 * "abc" auswerten soll, wird zuerst versucht, eine passende Methode des ersten Operanden zu benutzen. Existiert diese nicht oder gibt sie NotImplemented zurück, wird versucht, beim zweiten Operanden eine entsprechende Methode zu finden.

Allerdings muss der zweite Operand eine spezielle Methode für vertauschte Operanden implementieren.[ 97 ](Dass hier auf die Reihenfolge geachtet wird, ist wichtig, denn nicht bei allen Operationen ist die Reihenfolge der Operanden egal. Beispielsweise macht es einen Unterschied, ob "x" + "y" oder "y" + "x" ausgewertet wird. ) Tabelle 21.8 listet alle dafür verfügbaren Methodennamen und die entsprechenden Operatoren auf, wobei es für jeden der binären Operatoren eine Entsprechung gibt.

Operator Magic Method Operator Magic Method
+ __radd__(self, other) divmod() __rdivmod__(self, other)
- __rsub__(self, other) >> __rlshift__(self, other)
* __rmul__(self, other) << __rrshift__(self, other)
/ __rtruediv__(self, other) & __rand__(self, other)
// __rfloordiv__(self, other) | __ror__(self, other)
** __rpow__(self, other, [modulo]) ^ __rxor__(self, other)
% __rmod__(self, other)

Tabelle 21.8    Magic Methods für binäre Operatoren des rechten Operanden

Für nicht unterstützte Werte von other sollte auch hier NotImplemented zurückgegeben werden.

Erweiterte Zuweisungen

Es können auch die erweiterten Zuweisungen überladen werden, die eine arithmetische Operation mit einer Zuweisung verbinden. Bei einer erweiterten Zuweisung wird dem jeweiligen Operator ein Gleichheitszeichen nachgestellt:

>>> a = 10
>>> a += 5
>>> a
15

Standardmäßig verwendet Python für solche Zuweisungen den Operator selbst, sodass a += 5 intern wie a = a + 5 ausgeführt wird. Diese Vorgehensweise hat für komplexe Datentypen wie beispielsweise Listen den Nachteil, dass immer eine neue Liste erzeugt werden muss. Deshalb können Sie gezielt die erweiterten Zuweisungen anpassen, um die Effizienz des Programms zu verbessern. Außerdem wird bei der Anwendung einer erweiterten Zuweisung auf eine Instanz eines mutablen Datentyps erwartet, dass die Instanz selbst verändert und keine neue erzeugt wird.

In Tabelle 21.9 finden Sie alle Operatoren für erweiterte Zuweisungen und die entsprechenden Methoden:

Operator Magic Method Operator Magic Method
+= __iadd__(self, other) %= __imod__(self, other)
-= __isub__(self, other) >>= __ilshift__(self, other)
*= __imul__(self, other) <<= __irshift__(self, other)
/= __itruediv__(self, other) &= __iand__(self, other)
//= __ifloordiv__(self, other) |= __ior__(self, other)
**= __ipow__(self, other, [modulo]) ^= __ixor__(self, other)

Tabelle 21.9    Methoden für die erweiterte Zuweisung

[»]  Hinweis

Auch wenn die Operatoren für die erweiterte Zuweisung die Instanz self verändern, müssen sie eine Referenz auf das Ergebnis der Berechnung, in diesem Fall also self, zurückgeben.

Unäre Operatoren

Mit den folgenden Methoden werden die unären Operatoren überladen. Unäre Operatoren erwarten im Gegensatz zu den binären Operatoren nur einen Operanden.

Zu den unären Operatoren zählen die Vorzeichen + und -, die Built-in Function abs zur Bestimmung des absoluten Wertes und die Tilde ~, um das Komplement eines Wertes zu berechnen:

Operator Magic Method Operator Magic Method
+ __pos__(self) abs __abs__(self)
- __neg__(self) ~ __invert__(self)

Tabelle 21.10    Magic Methods für die unären Operatoren

Die Methoden sollten bei erfolgreicher Rechnung das Ergebnis zurückgeben. Ist es nicht möglich, den Operanden other zu verarbeiten, sollte NotImplemented zurückgegeben werden.

 
Zum Seitenanfang

21.7.3    Datentypen emulieren Zur vorigen ÜberschriftZur nächsten Überschrift

In Python entscheiden die Methoden, die ein Datentyp implementiert, zu welcher Kategorie von Datentypen er gehört. Deshalb ist es möglich, Ihre eigenen Datentypen beispielsweise wie numerische oder sequenzielle Datentypen »aussehen« zu lassen, indem sie die entsprechende Schnittstelle implementieren.

Dieses Konzept, den Typ einer Instanz anhand der vorhandenen Methoden und nicht der Klasse zu beurteilen, wird Duck-Typing genannt. Die Bezeichnung ist an ein Gedicht von James Whitcomb Riley[ 98 ](James Whitcomb Riley (1849–1916) war ein US-amerikanischer Dichter. ) angelehnt, dessen deutsche Übersetzung folgendermaßen lautet:

»Wenn ich einen Vogel sehe, der wie eine Ente läuft, schwimmt und quakt, so nenne ich diesen Vogel eine Ente.«[ 99 ](Englisches Original: »When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.« )

Übertragen auf die Instanzen in einem Programm bedeutet dies, dass beispielsweise alle Instanzen wie Zahlen behandelt werden, die sich genauso wie andere Zahlen verhalten. Ob es sich um Instanzen der Typen int, float oder complex handelt, ist dabei egal. Insbesondere ist es möglich, eine eigene Klasse zu definieren, die sich ebenfalls wie eine Zahl verhält und somit auch wie eine solche behandelt wird.

Dabei gilt das Prinzip, nicht mit Gewalt möglichst alle Operatoren und Methoden zu implementieren, sondern nur solche, die für die Klasse Sinn ergeben. Alle anderen Methoden sollten entweder gar nicht implementiert werden oder NotImplemented zurückgeben.

Sie werden im Folgenden die Methoden kennenlernen, die ein Datentyp implementieren muss, um nach dem Duck-Typing ein numerischer Datentyp zu sein. Außerdem werden die Schnittstellen von Sequenzen und Mappings behandelt.

Numerische Datentypen emulieren

Ein numerischer Datentyp sollte möglichst viele arithmetische Operatoren implementieren. Außerdem kann er Methoden definieren, um ihn in andere numerische Datentypen zu überführen, falls dies möglich ist.

Tabelle 21.11 gibt Ihnen einen Überblick über die möglichen Methoden.

Name oder Kurzbeschreibung Beschreibung
arithmetische Operatoren Rechenoperatoren für die Summe, Differenz, den Quotienten etc.
__complex__ Umwandlung nach complex
__int__ Umwandlung nach int
__float__ Umwandlung nach float
__round__ Rundung des Wertes
__index__ Verwendung als Index

Tabelle 21.11    Besondere Methoden, die ein numerischer Datentyp nach Möglichkeit definieren sollte

Kontext-Manager implementieren

Unter einem Kontext-Manager versteht man eine Instanz, die in Zusammenhang mit der with-Anweisung verwendet werden kann. Näheres zu with erfahren Sie in Kapitel 24, »Kontextobjekte«.

Um mit with als Kontext-Manager verwendet werden zu können, müssen zwei Methoden implementiert werden, die Tabelle 21.12 auflistet.

Name Beschreibung
__enter__(self) Baut den Kontext auf und gibt das Objekt zurück, mit dem gearbeitet werden soll.
__exit__(self, ...) Räumt nach Verlassen des Körpers der with-Anweisung auf.

Tabelle 21.12    Methoden für Kontext-Manager

Container emulieren

Mithilfe der folgenden Methoden ist es möglich, eigene Container-Datentypen zu erzeugen. Unter einem Container (engl. to contain, dt. »enthalten«), versteht man eine Instanz, die ihrerseits weitere Instanzen enthalten kann. Beispiele sind die Liste, das Dictionary oder die Menge.

Dabei wird grundsätzlich zwischen sequenziellen Containern, deren Elemente sich über ganze Zahlen[ 100 ](Dabei sollten die Elemente, bei 0 beginnend, fortlaufend durchnummeriert sein. ) ansprechen lassen, und Mapping-Containern, deren Indizes beliebige Gestalt haben können, unterschieden.

Methoden für allgemeine Container

Zunächst gibt es einen Satz von Methoden, den sowohl sequenzielle als auch Mapping-Container implementieren sollten.

Methode Beschreibung
__len__(self) Liefert die Anzahl der Elemente in dem Container als ganze Zahl zurück.
__getitem__(self, key) Liest ein Element oder mehrere Elemente aus dem Container, wenn der Operator [] verwendet wird.
__setitem__(self, key, value) Verändert das Element des Containers, das dem Schlüssel key zugeordnet ist.
__delitem__(self, key) Entfernt das Element mit dem Index key aus dem Container.
__iter__(self) Muss einen Iterator über die Werte des sequenziellen Containers bzw. die Schlüssel des Mapping-Containers zurückgeben.
Näheres über Iteratoren erfahren Sie in Abschnitt 23.3, »Iteratoren«.
__contains__(self, item) Prüft, ob item in dem Container enthalten ist.

Tabelle 21.13    Methoden, die alle Container-Datentypen implementieren können

Nun stellen wir Ihnen die Methoden vor, die speziell für sequenzielle Container vorgesehen sind.

Methoden für sequenzielle Container

Alle sequenziellen Container sollten zusätzlich zu den allgemeinen Methoden für Container die Methoden für die Addition (Verkettung) und Multiplikation (Wiederholung) implementieren.

Methoden Beschreibung
__add__(self, other)
__radd__(self, other)
__iadd__(self, other)
Verkettet die Sequenz mit der Sequenz other.
Dabei sollte __iadd__ im Fall eines mutablen Datentyps die von self referenzierte Instanz verändern, also in-place arbeiten.
__mul__(self, other)
__rmul__(self, other)
__imul__(self, other)
Sollte eine Sequenz erzeugen, die aus der von self referenzierten Instanz dadurch hervorgeht, dass sie other-mal wiederholt wird.
Für Strings sieht das beispielsweise folgendermaßen aus:
>>> 5*"a"
'aaaaa'

Tabelle 21.14    Allgemeine Methoden für sequenzielle Container

Mutable Sequenzen sollten zusätzlich noch die in Tabelle 21.15 gezeigten Methoden definieren. Für Beispiele dieser Methoden können Sie sich den Datentyp list in Abschnitt 13.2, »Listen – list«, anschauen.

Methoden Beschreibung
append(x) Hängt x an das Ende der Sequenz an.
count(x) Zählt die Vorkommen von x in der Sequenz.
index(x, [i, j]) Liefert den Index des ersten Vorkommens von x in der Sequenz.
Mit den optionalen Parametern i und j kann dabei der Suchbereich eingegrenzt werden.
extend(s) Erweitert die Sequenz um die Elemente der Sequenz s.
insert(i, x) Fügt das Element x an der Stelle i in die Sequenz ein.
pop([i]) Liefert das i-te Element der Sequenz und entfernt es aus dieser. Wird i nicht angegeben, wird das letzte Element zurückgegeben und anschließend entfernt.
remove(x) Entfernt das erste Vorkommen von x in der Sequenz aus dieser.
__reversed__(self) Liefert einen Iterator zum umgekehrten Durchlaufen des sequenziellen Datentyps. Dabei wird die Sequenz nicht verändert.
reverse() Dreht die Reihenfolge der Sequenz in-place um.
sort([key, reverse]) Sortiert die Sequenz in-place.

Tabelle 21.15    Methoden für mutable Sequenzen

Methoden für Mapping-Container

Alle Mapping-Datentypen sollten zusätzlich zu den Methoden für allgemeine Container weitere Methoden implementieren, die in Tabelle 21.16 aufgelistet sind.[ 101 ](Wenn Ihnen die hier angegebenen Beschreibungen nicht ausführlich genug sind, können Sie sich noch einmal den Abschnitt 14.1, »Abschnitt 14.1«, ansehen. )

Methode Bedeutung
m.keys() Gibt einen Iterator über die Schlüssel von m zurück.
m.values() Gibt einen Iterator über die Werte von m zurück.
m.items() Gibt einen Iterator über die Schlüssel-Wert-Paare von m zurück.
m.has_key(k) Prüft, ob der Schlüssel k in m existiert.
m.get(k, [d]) Wenn der Schlüssel k in m existiert, wird m[k] zurückgegeben, ansonsten d.
m.clear() Entfernt alle Elemente aus m.
m.setdefault(k, [x]) Wenn der Schlüssel k in m existiert, wird m[k] zurückgegeben. Gibt es den Schlüssel k nicht in m, wird m[k] auf den Wert x gesetzt und x zurückgegeben.
m.pop(k, [d]) Wenn der Schlüssel k in m existiert, wird m[k] zurückgegeben und danach mit del gelöscht. Gibt es den Schlüssel k nicht in m, wird d zurückgegeben.
m.popitem() Gibt ein willkürlich ausgewähltes Schlüssel-Wert-Paar von m zurück und entfernt es anschließend aus m.
m.copy() Gibt eine Kopie von m zurück.
m.update(b) Übernimmt alle Schlüssel-Wert-Paare von b in m. Vorhandene Einträge werden dabei überschrieben.

Tabelle 21.16    Methoden für Mapping-Typen

 


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