Fortgeschrittenes Python: Was sind magische Methoden?

Dieser Artikel beleuchtet die Python-Spezialmethoden, die jeder Python-Programmierer kennen muss

Farhad Malik

Follow

May 16, 2020 – 14 min read

Die magischen Methoden helfen uns, unsere Anwendungen zu bereichern. Sie fügen unserem Python-Code indirekt Magie hinzu. Dies ist ein Thema für fortgeschrittene Python-Entwickler und ich empfehle es allen, die die Programmiersprache Python verwenden oder dies vorhaben.

Die magischen Methoden geben uns mehr Kontrolle darüber, wie sich unsere Anwendung verhält.

Dieser Artikel soll erklären, was magische Methoden sind und wie sie zum Erstellen von Python-Anwendungen verwendet werden können. Er gibt einen Überblick über die am häufigsten verwendeten magischen Methoden für eine Reihe von Datentypen.

Der Zweck der Darstellung der wichtigsten magischen Methoden ist es, dass wir verstehen, ob wir diese Methoden in unseren benutzerdefinierten Klassen überschreiben wollen, um die Anwendungen zu bereichern.

Magische Methoden machen die Programmiersprache Python extrem leistungsfähig

Foto von Rodion Kutsaev auf Unsplash

Was sind die magischen Methoden von Python?

Die magischen Methoden von Python sind auch als spezielle Methoden oder Dunder-Methoden bekannt. Sie sind von doppelten Unterstrichen umgeben, z.B. __init__().

Ein Objekt kann mehrere magische Methoden haben.

Erinnern Sie sich, dass alles in Python ein Objekt ist, einschließlich einer Variablen/Funktion/Klasse usw. Die Objekte sind Pythons Abstraktion für Daten.

Die magischen Methoden werden verwendet, um neue Objekte zu konstruieren und zu initialisieren, sie helfen uns, ein Objekt als Wörterbuch abzurufen, sie werden verwendet, um ein Objekt neben anderen Operationen zu löschen. Sie werden verwendet, wenn der +-Operator aufgerufen wird, oder sogar, wenn wir ein Objekt als String darstellen wollen.

Obwohl jede Methode in Python öffentlich ist, besteht die Kodierungskonvention darin, alle privaten Methoden mit doppelten Unterstrichen zu umgeben __<Methode>__()

Das bedeutet, dass die magischen Methoden als private Methoden gedacht sind. Es bedeutet auch, dass der Aufrufer eines Objekts die Methode nicht direkt aufrufen sollte, da die Methode intern von der Klasse aufgerufen werden soll, die die magische Methode besitzt.

Wir können die magischen Methoden überschreiben, um unsere eigene benutzerdefinierte Funktionalität bereitzustellen.

Foto von Cristian Escobar auf Unsplash

Ich werde die Konzepte der magischen Methoden erklären, indem ich eine benutzerdefinierte Klasse erstelle, und dann werde ich einen Überblick über die wichtigsten magischen Methoden in einem Integer, String, List und einem Dictionary geben.

Im weiteren Verlauf des Artikels wird ein viel klareres Bild davon entstehen, warum die magischen Methoden existieren und wie man sie verwendet.

Wenn Sie die Programmiersprache Python vom Anfänger bis zum Fortgeschrittenen verstehen wollen, dann empfehle ich Ihnen den folgenden Artikel:

Ich werde das Thema der magischen Methoden beginnen, indem ich eine benutzerdefinierte Klasse erstelle und dann erkläre ich, wie die magischen Methoden verwendet werden.

Der Anwendungsfall zum Verständnis der magischen Methoden

Im folgenden Ausschnitt habe ich eine Klasse namens Human erstellt und dann eine Instanz der Klasse Human instanziiert.

Dieser Codeschnipsel soll uns helfen, die magischen Methoden zu verstehen.

Beachten Sie, dass die ID des Objekts Human ein Integer-Typ ist, das Attribut name vom Typ string, die Eigenschaft addresses vom Typ list und die Eigenschaft maps vom Typ dictionary.

Ich habe die magischen Methoden in verschiedene Datentyp-Abschnitte gruppiert, um das Lesen zu erleichtern. Allerdings sind die gleichen magischen Methoden in verschiedenen Datentypen zu finden.

Die magischen Methoden können überschrieben werden, um die Funktionalität zu erweitern und eine benutzerdefinierte Logik zu erstellen, die den Geschäftsanforderungen am besten entspricht.

Klasse:

Lassen Sie uns die magischen Methoden verstehen, die mit dem menschlichen Objekt verbunden sind. Wenn ich die Methode dir(human) ausführe, werden alle Funktionen und Attributnamen des menschlichen Objekts aufgelistet.

Es gibt insgesamt 29 Methoden/Attribute, die mit dem menschlichen Objekt verbunden sind. Davon sind 26 die magischen Methoden.

Das ist eine ziemlich große Anzahl von speziellen Methoden. Diese Methoden werden vom Basistyp der Klasse Human geerbt. Daher sind sie die vordefinierten Methoden, die wir verwenden/überschreiben können, um die Klassen zu bereichern.

Im nächsten Teil des Abschnitts werden die wichtigsten magischen Methoden erklärt.

__delattr__

Diese Methode wird aufgerufen, wenn wir versuchen, ein Attribut aus einer Klasse zu löschen.

Wir können die Funktionalität außer Kraft setzen, indem wir die Methode in der Klasse Human implementieren:

def __delattr__(self, item):
print('Deleting attribute ' + item)
return object.__delattr__(self, item)

Jetzt wird jedes Mal, wenn wir versuchen, ein Attribut zu löschen, die Meldung angezeigt: Deleting attribute

Es gibt auch die Methode __setattr__(), um einem Attribut einen Wert zuzuweisen und __getattr__(), um einen Wert vom Attribut zu erhalten.

Ein Anwendungsfall von__delattr__() kann sein, dass bestimmte Attribute eines Objekts nicht gelöscht werden sollen oder wenn wir bestimmte Aktionen durchführen wollen, wenn ein bestimmtes Attribut gelöscht wird.

__dict__

Diese Methode gibt ein Wörterbuch zurück, das das Objekt repräsentiert. Die Schlüssel des Wörterbuchs sind die Attribute des Objekts und die Werte sind die Werte der Attribute.

Als Instanz:

human = Human(2, 'Malik').__dict__
print(human)

Der obige Code liefert:

{‚id‘: 2, ’name‘: ‚Malik‘, ‚addresses‘: , ‚maps‘: {}}

__dir__

Wir können die dir()-Methode außer Kraft setzen, indem wir die __dir__()-Methode in der Klasse außer Kraft setzen. Als Beispiel können wir die internen Methoden aus dem Ergebnis entfernen, das von der dir()-Methode zurückgegeben wird:

__eq__

Diese Methode wird aufgerufen, wenn wir versuchen, die == Operation durchzuführen. Nehmen wir an, dass zwei menschliche Objekte gleich sind, wenn ihr id-Attribut gleich ist, auch wenn ihr Name unterschiedlich ist:

Wir können die __eq__()-Methode überschreiben, um diese Funktionalität zu erreichen:

def __eq__(self, other):
return self.id == other.id

Dies wird nun True zurückgeben:

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
print(first == second)

__format__

Wenn wir versuchen, string.format() auszuführen, wird intern die Methode __format__() aufgerufen.

__ge__

Angenommen, es gibt zwei Human-Objekte:

first = Human(1, 'Farhad')
second = Human(2, 'Malik')

Angenommen, unser Projekt hat eine Regel, dass ein Human-Objekt mit einer größeren Id als das andere Human-Objekt betrachtet wird. Daher können wir die __gt__()-Methode überschreiben und die benutzerdefinierte Logik implementieren:

def __ge__(self, other):
return self.id >= other.id

Dies wird nun False zurückgeben, da die Id des zweiten menschlichen Objekts größer ist als die des ersten menschlichen Objekts:

print(first >= second)
Returns False

Es gibt auch eine __lt__()-Methode, die ausgeführt wird, wenn der Operator ≤ ausgeführt wird.

__hash__

Hashing wird verwendet, um ein Objekt in eine ganze Zahl umzuwandeln. Hashing wird durchgeführt, wenn wir versuchen, ein Element in einem Wörterbuch/Set zu setzen.

Ein guter Hashing-Algorithmus führt zu einer geringeren Anzahl von Hashing-Kollisionen. Wir können unseren eigenen Hash-Algorithmus bereitstellen, indem wir die Methode __hash__() überschreiben.

Betrachten wir, dass die ID des Human-Objekts in unserer Anwendung eindeutig sein soll. Der __hash__()-Algorithmus kann überschrieben werden, um die self.id als Hash-Integer zurückzugeben:

def __hash__(self):
return self.id

Wir können zwei Objekte erstellen und sie in einer Set-Sammlung speichern. Wenn wir die Länge der Menge abfragen, werden wir zwei Elemente in der Menge erwarten, da beide Objekte eine unterschiedliche id haben.

first = Human(1, 'Farhad')
second = Human(2, 'Malik')
my_set = set()
print(len(my_set))
#returns 2

Wenn wir nun die id für beide Objekte auf 1 setzen und die Übung wiederholen, werden wir nur 1 Element in der Menge sehen, da beide Objekte denselben Hash-Schlüssel haben, da ihr id-Attribut dasselbe ist, auch wenn ihr name-Attribut unterschiedlich ist.

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
my_set = set()
print(len(my_set))
#returns 1

__init__

Die Methode __init__() wird ausgeführt, wenn wir eine neue Instanz einer Klasse durch den Aufruf ihres Konstruktors instanziieren wollen.

Als Instanz, wenn wir versucht haben:

human = Human(1, 'farhad')

Dann wurde die Methode __init__() ausgeführt.

Wir können die Funktionalität überschreiben und auch unsere eigenen benutzerdefinierten Argumente und Verhaltensweisen in ihr übergeben.

Als Instanz ist die __init__()-Methode der Klasse Human:

def __init__(self, id, name, addresses=, maps={}):
self.id = id
self.name = name
self.addresses = addresses
self.maps = maps

Foto von Cederic X auf Unsplash

__init_subclass__

Dies ist einer der Anwendungsfälle der Metaklasse. Wenn eine Klasse untergeordnet wird und ihr Objekt erstellt wird, wird die Methode __init_subclass__() aufgerufen.

Im Wesentlichen informiert die Methode die Elternklasse, dass sie untergeordnet wurde. Dieser Hook kann dann alle Unterklassen einer bestimmten Klasse initialisieren. Daher wird die Methode zur Registrierung von Unterklassen und zur Zuweisung von Standardwerten für die Attribute der Unterklassen verwendet.

Ich werde in meinem nächsten Artikel erklären, wie Metaklassen funktionieren.

__new__

Wenn wir eine neue Instanz einer Klasse instanziieren/erzeugen wollen, wird die Methode __new__(cls) ausgeführt.

Betrachten wir als Beispiel, dass wir ‚Human creating…‘ ausgeben wollen, wenn der Konstruktor Human() aufgerufen wird.

Wir können die Funktionalität der __new__(cls)-Methode wie unten gezeigt überschreiben:

def __new__(cls, *args, **kwargs):
print('Human creating...')
return object.__new__(cls)

Als Ergebnis wird ‚Human creating…‘ gedruckt, wenn wir versuchen, eine Instanz zu erstellen:

human = Human(1, 'Farhad', , {'London':2, 'UK':3})

__sizeof__

Diese Methode wird aufgerufen, wenn wir sys.getsizeof() ausführen. Sie gibt die Größe des Objekts im Speicher in Bytes zurück.

__str__

Sie gibt aus: id=1. name=Farhad.

Die Funktion __str__() sollte versuchen, eine benutzerfreundliche Darstellung des Objekts zurückzugeben.

__weakref__

Dieses __weakref__-Objekt gibt die Liste der schwachen Referenzen auf das Zielobjekt zurück. Im Wesentlichen hilft es der Garbage Collection, schwache Referenzen darüber zu informieren, dass der Referent eingesammelt wurde. Daher verhindert es, dass die Objekte auf die zugrunde liegenden Zeiger zugreifen.

Foto von Yousef Espanioly auf Unsplash

Ganzzahl

Das bringt uns zum nächsten Abschnitt des Artikels. Wir können sehen, dass die id-Eigenschaft des Objekts human vom Typ int ist. Wenn wir dann dir() auf der id-Eigenschaft ausführen und die Methoden herausfiltern, die von doppelten Unterstrichen umgeben sind, werden wir feststellen, dass es insgesamt 62 magische Methoden gibt.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.id)))))

Ich werde die wichtigsten Methoden hier erklären:

__add__

Diese Methode wird aufgerufen, wenn wir versuchen, zwei Zahlen zu addieren.

Als Beispiel:

human.id + 2 ist dasselbe wie human.id.__add__(2)

__und__

Diese Methode wird ausgeführt, wenn wir versuchen, den & Operator zu verwenden, z.g.:

return self & another_value

__bool__

Diese Methode wird ausgeführt, wenn wir versuchen, die boolesche Prüfung auf einem Objekt durchzuführen, z.B.

self != 123

__floordiv__

Diese Methode wird ausgeführt, wenn wir den //Operator ausführen.

Foto von Johannes Plenio auf Unsplash

__getnewargs__

Gelegentlich picken wir Objekte in Python. Pickling erzeugt eine Byte-Stream-Repräsentation eines Objekts im Speicher, die bei Bedarf auf der Festplatte gespeichert werden kann.

Die Methode __getnewargs__() informiert den Pickling-Prozess darüber, wie er das gepickte Objekt zurückladen muss, um das Zielobjekt neu zu erstellen. Insbesondere, wie das Objekt durch Übergabe der Argumente an die new()-Methode erstellt werden muss. Daher der Name ‚get new args‘.

__index__

Anschließend kann ein Objekt durch Ausführen der Methode __index__() in eine Ganzzahl umgewandelt werden. Wir können die Methode auch überschreiben und unsere eigene Funktionalität bereitstellen, wie der Index erzeugt werden muss.

__invert__

Diese Methode wird ausgeführt, wenn wir den ~-Operator verwenden.

Als Instanz:

first = Human(1, 'Farhad')
print(~first.id)

Es ist dasselbe wie die Ausführung:

first = Human(1, 'Farhad')
print(first.id.__invert__())

__lshift__

Diese Methode gibt uns eine Linksverschiebung eines Wertes z.B. self << Wert. Wir können den <<-Operator überladen, indem wir die Methode __lshift__() überschreiben.

Hinweis: __rshift__() wird ausgeführt, wenn wir den >>-Operator ausführen.

__mod__

Diese Methode wird aufgerufen, wenn wir den %-Operator verwenden.

__neg__

Diese Methode wird ausgeführt, wenn wir den negativen – Operator verwenden, z.B.

first.id — second.id

__subclasshook__

Diese Methode kann überschrieben werden, um die Methode issubclass() anzupassen. Im Wesentlichen gibt sie True zurück, wenn eine Klasse eine Unterklasse ist, und False, wenn sie es nicht ist. Die Methode gibt auch NotImplemented zurück, wodurch der bestehende Algorithmus verwendet werden kann.

Die Methode kann das Ergebnis der Methode issubclass() anpassen.

Foto von Patrick Selin auf Unsplash

String

Damit kommen wir zum nächsten Abschnitt des Artikels.

Wir können sehen, dass die Eigenschaft name des Objekts human vom Typ string ist. Wenn wir dann dir(human.name) auf die Eigenschaft anwenden und die Methoden herausfiltern, die von doppelten Unterstrichen umgeben sind, werden wir feststellen, dass es insgesamt 33 magische Methoden gibt.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.name)))))

Ich werde hier die 4 wichtigsten Methoden erklären, da der Rest des Artikels die meisten magischen Methoden bereits hervorgehoben hat.

__contains__

Diese Methode wird ausgeführt, wenn wir prüfen wollen, ob ein bestimmtes Zeichen existiert.

__len__

Diese Methode gibt die Länge der Zeichenkette zurück. Sie wird ausgeführt, wenn wir die Methode len() ausführen.

Wenn wir nur bestimmte Zeichen zählen wollen, um die Länge der Zeichenkette zu berechnen, dann kann die Methode __()__ überschrieben werden, um diese Funktionalität bereitzustellen.

__repr__

Diese Methode wird ausgeführt, wenn wir eine entwicklerfreundliche Darstellung eines Objekts erstellen wollen.

Beachten Sie, dass __repr__ ausgeführt wird, wenn wir print(object) ausführen, wenn wir keine __str__()-Implementierung in unserer Klasse haben.

def __repr__(self):
return f'id={self.id} ({type(self.id)}). name={self.name} ({type(self.name)})'print(Human(1, 'Farhad'))

Das wird drucken:

id=1 (<Klasse ‚int‘>). name=Farhad (<Klasse ’str‘>)

Die Methode __repr__() soll die offizielle Repräsentation eines Objekts erzeugen.

__iadd__

Gelegentlich verwenden wir neben der Zuweisung einen Additionsoperator e.g.

self.id += 1

Das entspricht self.id = self.id + 1

Die Methode iadd() wird ausgeführt, wenn wir die Addition mit der Zuweisung durchführen.

Außerdem wird die Methode __ipow__() ausgeführt, wenn **= durchgeführt wird.

Die magische Methode __iand__() dient dazu, ein bitweises UND mit einer Zuweisung durchzuführen, und __ior__() wird aufgerufen, wenn wir versuchen, != zu machen, wie zum Beispiel: i != j

Liste

Damit kommen wir zum nächsten Abschnitt des Artikels. Die Eigenschaft addresses des Objekts human ist vom Typ list. Wenn wir nun dir(human.addresses) auf die Eigenschaft addresses anwenden und die Methoden herausfiltern, die von doppelten Unterstrichen umgeben sind, werden wir feststellen, dass es insgesamt 35 magische Methoden gibt.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.addresses)))))

Ich werde die wichtigsten Methoden hier erläutern:

__reduce__

Wenn ein Objekt gepickelt wird, wird die Methode __reduce__() ausgeführt, um ein Objekt zurückzugeben, das dem Pickle hilft, zu verstehen, wie es wieder aufgebaut werden kann.

__reduce_ex__

Die __reduce_ex__()-Methode wird vom Pickle gegenüber der __reduce__()-Methode bevorzugt.

Die __reduce_ex__()-Methode nimmt ein ganzzahliges Argument, das die Protokollversion ist. Sie bietet Abwärtskompatibilität für Picking und wird verwendet, um den gepickten Byte-Stream zum Objekt zu konstruieren.

__reversed__

Die Methode __reversed__() wird ausgeführt, wenn wir versuchen, eine Sammlung in umgekehrter Reihenfolge umzukehren.

Sie wird ausgeführt, wenn reversed(collection) oder collection.reverse() aufgerufen wird. Manchmal beschließen wir, die Funktionalität der Methode reversed() zu ändern.

Indem wir die Methode __reversed__() überschreiben, können wir das gewünschte Ergebnis erzielen.

Dictionary

Dies bringt uns zum fünften Abschnitt des Artikels.

Dictionary ist einer der wichtigsten eingebauten Typen in Python. Wenn wir dir(human.maps) ausführen und die Methoden herausfiltern, die von doppelten Unterstrichen umgeben sind, werden wir feststellen, dass es insgesamt 29 magische Methoden gibt.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.maps)))))

Unter den 29 magischen Methoden werde ich hier die 4 wichtigsten Methoden erklären:

__delitem__

Diese Methode wird ausgeführt, wenn wir ein Element löschen, z.g.

del dictionary

__getitem__

Diese Methode wird ausgeführt, wenn wir versuchen, ein Element für einen Schlüssel zu erhalten:

item = dictionary

__setitem__

Diese Methode wird ausgeführt, wenn wir versuchen, ein Element im Wörterbuch zu setzen:

dictionary = item

__iter__

Diese Methode liefert einen Iterator für die Sammlung. Ein Iterator hilft uns, über eine Sammlung zu iterieren.

Wir können überschreiben, wie der Iterator() ausgeführt wird, indem wir die Methode __iter__() überschreiben.

Zuletzt noch eine Anmerkung zu __call__()

Was wäre, wenn wir unser Objekt aufrufbar machen wollten? Nehmen wir an, wir wollten aus dem Human-Objekt eine aufrufbare Human()-Funktion machen?

Die__call__()-Methode erlaubt es uns, die Klassen wie Funktionen zu behandeln.

human = Human(1, 'Farhad')
human()

Wir können diese Funktionalität erreichen, indem wir die magische __call__()-Methodenimplementierung in unserer Human-Klasse bereitstellen.

Das wird Folgendes ausgeben:

Sie haben versucht,
Argumente aufzurufen: ()
Schlüsselwort-Argumente: {}
id=1 (<Klasse ‚int‘>). name=Farhad (<Klasse ’str‘>)
Aufruf abgeschlossen

Durch Überschreiben der __call__()-Methode können wir nun einen Dekorator implementieren, um ein Objekt als Funktion zurückzugeben oder sogar jene Bibliotheken aufzurufen, die Funktionen als Argumente akzeptieren, indem wir die tatsächlichen Objekte übergeben.

Foto von Dollar Gill auf Unsplash

Zusammenfassung

Dies ist ein Thema für Python-Entwickler auf fortgeschrittenem Niveau und ich empfehle es jedem, der die Programmiersprache Python verwendet oder dies vorhat.

Dieser Artikel soll erklären, was magische Methoden sind und wie sie zum Erstellen von Python-Anwendungen verwendet werden können. Er bietet einen Überblick über die am häufigsten verwendeten magischen Methoden in einer benutzerdefinierten Klasse, Ganzzahlen, Zeichenketten, Listen und Wörterbuch-Datentypen.

Obwohl jede Methode in Python öffentlich ist, besteht die Kodierungskonvention darin, alle privaten Methoden mit doppelten Unterstrichen zu umgeben __<Methode>__()

Das bedeutet, dass magische Methoden als private Methoden gedacht sind. Es bedeutet auch, dass der Aufrufer eines Objekts die Methode nicht direkt aufrufen sollte und die Methode intern von der Klasse aufgerufen wird, die die magische Methode besitzt. Magische Methoden geben uns mehr Kontrolle darüber, wie sich unsere Anwendung verhält.

Die magischen Methoden können überschrieben werden, um die Funktionalität zu erweitern und eine benutzerdefinierte Logik zu erstellen, die den Geschäftsanforderungen am besten entspricht.

Der Zweck der Darstellung der wichtigsten magischen Methoden ist es, dass wir verstehen, ob wir diese Methoden in unseren benutzerdefinierten Klassen überschreiben wollen, um die Anwendungen zu erweitern.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.