Neudefinieren von speziellen Methoden

PythonPythonBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab werden Sie lernen, wie Sie das Verhalten von Objekten durch die Neudefinition spezieller Methoden anpassen können. Sie werden auch die Art und Weise ändern, wie benutzerdefinierte Objekte ausgegeben werden, und Objekte vergleichbar machen.

Zusätzlich werden Sie lernen, einen Kontext-Manager (Context Manager) zu erstellen. Die Datei, die in diesem Lab bearbeitet werden soll, ist stock.py.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/FileHandlingGroup(["File Handling"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python/FunctionsGroup -.-> python/build_in_functions("Build-in Functions") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("Encapsulation") python/FileHandlingGroup -.-> python/file_opening_closing("Opening and Closing Files") python/FileHandlingGroup -.-> python/file_operations("File Operations") python/AdvancedTopicsGroup -.-> python/context_managers("Context Managers") subgraph Lab Skills python/build_in_functions -.-> lab-132496{{"Neudefinieren von speziellen Methoden"}} python/classes_objects -.-> lab-132496{{"Neudefinieren von speziellen Methoden"}} python/encapsulation -.-> lab-132496{{"Neudefinieren von speziellen Methoden"}} python/file_opening_closing -.-> lab-132496{{"Neudefinieren von speziellen Methoden"}} python/file_operations -.-> lab-132496{{"Neudefinieren von speziellen Methoden"}} python/context_managers -.-> lab-132496{{"Neudefinieren von speziellen Methoden"}} end

Verbessern der Objektrepräsentation mit __repr__

In Python können Objekte auf zwei verschiedene Arten als Zeichenketten (Strings) dargestellt werden. Diese Darstellungen dienen unterschiedlichen Zwecken und sind in verschiedenen Szenarien nützlich.

Der erste Typ ist die Zeichenkettenrepräsentation. Diese wird von der str()-Funktion erstellt, die automatisch aufgerufen wird, wenn Sie die print()-Funktion verwenden. Die Zeichenkettenrepräsentation ist für die menschliche Lesbarkeit konzipiert. Sie stellt das Objekt in einem Format dar, das für uns leicht zu verstehen und zu interpretieren ist.

Der zweite Typ ist die Code - Repräsentation. Diese wird von der repr()-Funktion generiert. Die Code - Repräsentation zeigt den Code, den Sie schreiben müssten, um das Objekt neu zu erstellen. Sie geht eher darum, eine präzise und eindeutige Möglichkeit zur Darstellung des Objekts im Code bereitzustellen.

Schauen wir uns ein konkretes Beispiel an, indem wir die eingebaute date-Klasse von Python verwenden. Dies wird Ihnen helfen, den Unterschied zwischen der Zeichenketten- und der Code - Repräsentation in der Praxis zu verstehen.

>>> from datetime import date
>>> d = date(2008, 7, 5)
>>> print(d)              ## Uses str()
2008-07-05
>>> d                     ## Uses repr()
datetime.date(2008, 7, 5)

In diesem Beispiel ruft Python bei print(d) die str()-Funktion auf das date-Objekt d auf, und wir erhalten ein für Menschen lesbares Datum im Format YYYY-MM-DD. Wenn wir einfach d in der interaktiven Shell eingeben, ruft Python die repr()-Funktion auf, und wir sehen den Code, der benötigt wird, um das date-Objekt neu zu erstellen.

Sie können die repr()-Zeichenkette auf verschiedene Weise explizit abrufen. Hier sind einige Beispiele:

>>> print('The date is', repr(d))
The date is datetime.date(2008, 7, 5)
>>> print(f'The date is {d!r}')
The date is datetime.date(2008, 7, 5)
>>> print('The date is %r' % d)
The date is datetime.date(2008, 7, 5)

Jetzt wenden wir dieses Konzept auf unsere Stock-Klasse an. Wir werden die Klasse verbessern, indem wir die __repr__-Methode implementieren. Diese spezielle Methode wird von Python aufgerufen, wenn es die Code - Repräsentation eines Objekts benötigt.

Um dies zu tun, öffnen Sie die Datei stock.py in Ihrem Editor. Fügen Sie dann die __repr__-Methode zur Stock-Klasse hinzu. Die __repr__-Methode sollte eine Zeichenkette zurückgeben, die den Code zeigt, der benötigt wird, um das Stock-Objekt neu zu erstellen.

def __repr__(self):
    return f"Stock('{self.name}', {self.shares}, {self.price})"

Nachdem Sie die __repr__-Methode hinzugefügt haben, sollte Ihre vollständige Stock-Klasse jetzt so aussehen:

class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

    def cost(self):
        return self.shares * self.price

    def sell(self, shares):
        self.shares -= shares

    def __repr__(self):
        return f"Stock('{self.name}', {self.shares}, {self.price})"

Jetzt testen wir unsere modifizierte Stock-Klasse. Öffnen Sie eine interaktive Python-Shell, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

python3

Sobald die interaktive Shell geöffnet ist, versuchen Sie die folgenden Befehle:

>>> import stock
>>> goog = stock.Stock('GOOG', 100, 490.10)
>>> goog
Stock('GOOG', 100, 490.1)

Sie können auch sehen, wie die __repr__-Methode mit einem Portfolio von Aktien funktioniert. Hier ist ein Beispiel:

>>> import reader
>>> portfolio = reader.read_csv_as_instances('portfolio.csv', stock.Stock)
>>> portfolio
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44), Stock('MSFT', 200, 51.23), Stock('GE', 95, 40.37), Stock('MSFT', 50, 65.1), Stock('IBM', 100, 70.44)]

Wie Sie sehen können, hat die __repr__-Methode unsere Stock-Objekte viel informativer gemacht, wenn sie in der interaktiven Shell oder im Debugger angezeigt werden. Sie zeigt jetzt den Code, der benötigt wird, um jedes Objekt neu zu erstellen, was für das Debugging und das Verständnis des Zustands der Objekte sehr nützlich ist.

Wenn Sie mit dem Testen fertig sind, können Sie den Python-Interpreter beenden, indem Sie den folgenden Befehl ausführen:

>>> exit()
✨ Lösung prüfen und üben

Vergleichbarkeit von Objekten mit __eq__

In Python ruft Python tatsächlich die spezielle Methode __eq__ auf, wenn Sie den ==-Operator verwenden, um zwei Objekte zu vergleichen. Standardmäßig vergleicht diese Methode die Identitäten der Objekte, was bedeutet, dass sie prüft, ob sie an der gleichen Speicheradresse gespeichert sind, anstatt ihren Inhalt zu vergleichen.

Schauen wir uns ein Beispiel an. Nehmen wir an, wir haben eine Stock-Klasse und erstellen zwei Stock-Objekte mit denselben Werten. Dann versuchen wir, sie mit dem ==-Operator zu vergleichen. So können Sie es in der Python-Shell tun:

Zuerst starten Sie die Python-Shell, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:

python3

Dann führen Sie in der Python-Shell den folgenden Code aus:

>>> import stock
>>> a = stock.Stock('GOOG', 100, 490.1)
>>> b = stock.Stock('GOOG', 100, 490.1)
>>> a == b
False

Wie Sie sehen können, hält Python die beiden Stock-Objekte a und b für unterschiedliche Objekte, obwohl sie die gleichen Werte für ihre Attribute (name, shares und price) haben, weil sie an verschiedenen Speicherorten gespeichert sind.

Um dieses Problem zu beheben, können wir die __eq__-Methode in unserer Stock-Klasse implementieren. Diese Methode wird jedes Mal aufgerufen, wenn der ==-Operator auf Objekte der Stock-Klasse angewendet wird.

Öffnen Sie jetzt erneut die Datei stock.py. Fügen Sie innerhalb der Stock-Klasse die folgende __eq__-Methode hinzu:

def __eq__(self, other):
    return isinstance(other, Stock) and ((self.name, self.shares, self.price) ==
                                         (other.name, other.shares, other.price))

Lassen Sie uns analysieren, was diese Methode tut:

  1. Zuerst verwendet sie die isinstance-Funktion, um zu prüfen, ob das other-Objekt eine Instanz der Stock-Klasse ist. Dies ist wichtig, weil wir nur Stock-Objekte mit anderen Stock-Objekten vergleichen möchten.
  2. Wenn other ein Stock-Objekt ist, vergleicht sie dann die Attribute (name, shares und price) sowohl des self-Objekts als auch des other-Objekts.
  3. Sie gibt nur True zurück, wenn beide Objekte Stock-Instanzen sind und ihre Attribute identisch sind.

Nachdem Sie die __eq__-Methode hinzugefügt haben, sollte Ihre vollständige Stock-Klasse so aussehen:

class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

    def cost(self):
        return self.shares * self.price

    def sell(self, shares):
        self.shares -= shares

    def __repr__(self):
        return f"Stock('{self.name}', {self.shares}, {self.price})"

    def __eq__(self, other):
        return isinstance(other, Stock) and ((self.name, self.shares, self.price) ==
                                             (other.name, other.shares, other.price))

Jetzt testen wir unsere verbesserte Stock-Klasse. Starten Sie die Python-Shell erneut:

python3

Führen Sie dann den folgenden Code in der Python-Shell aus:

>>> import stock
>>> a = stock.Stock('GOOG', 100, 490.1)
>>> b = stock.Stock('GOOG', 100, 490.1)
>>> a == b
True
>>> c = stock.Stock('GOOG', 200, 490.1)
>>> a == c
False

Toll! Jetzt können unsere Stock-Objekte richtig anhand ihres Inhalts und nicht ihrer Speicheradressen verglichen werden.

Die isinstance-Prüfung in der __eq__-Methode ist von entscheidender Bedeutung. Sie stellt sicher, dass wir nur Stock-Objekte vergleichen. Wenn wir diese Prüfung nicht hätten, könnte das Vergleichen eines Stock-Objekts mit etwas, das kein Stock-Objekt ist, Fehler verursachen.

Wenn Sie mit dem Testen fertig sind, können Sie die Python-Shell beenden, indem Sie den folgenden Befehl ausführen:

>>> exit()
✨ Lösung prüfen und üben

Erstellen eines Kontext-Managers

Ein Kontext-Manager ist ein spezieller Objekttyp in Python. In Python können Objekte verschiedene Methoden haben, die ihr Verhalten definieren. Ein Kontext-Manager definiert speziell zwei wichtige Methoden: __enter__ und __exit__. Diese Methoden arbeiten zusammen mit der with-Anweisung. Die with-Anweisung wird verwendet, um einen bestimmten Kontext für einen Codeblock einzurichten. Stellen Sie sich das wie die Erstellung einer kleinen Umgebung vor, in der bestimmte Dinge passieren, und wenn der Codeblock beendet ist, kümmert sich der Kontext-Manager um die Aufräumarbeiten.

In diesem Schritt werden wir einen Kontext-Manager erstellen, der eine sehr nützliche Funktion hat. Er wird die Standardausgabe (sys.stdout) temporär umleiten. Die Standardausgabe ist der Ort, an den die normale Ausgabe Ihres Python-Programms geht, normalerweise die Konsole. Indem wir sie umleiten, können wir die Ausgabe stattdessen in eine Datei senden. Dies ist praktisch, wenn Sie die Ausgabe speichern möchten, die sonst nur auf der Konsole angezeigt würde.

Zuerst müssen wir eine neue Datei erstellen, um unseren Kontext-Manager-Code zu schreiben. Wir werden diese Datei redirect.py nennen. Sie können sie mit dem folgenden Befehl im Terminal erstellen:

touch /home/labex/project/redirect.py

Jetzt, da die Datei erstellt ist, öffnen Sie sie in einem Editor. Sobald sie geöffnet ist, fügen Sie den folgenden Python-Code zur Datei hinzu:

import sys

class redirect_stdout:
    def __init__(self, out_file):
        self.out_file = out_file

    def __enter__(self):
        self.stdout = sys.stdout
        sys.stdout = self.out_file
        return self.out_file

    def __exit__(self, ty, val, tb):
        sys.stdout = self.stdout

Lassen Sie uns analysieren, was dieser Kontext-Manager tut:

  1. __init__: Dies ist die Initialisierungsmethode. Wenn wir eine Instanz der redirect_stdout-Klasse erstellen, übergeben wir ein Dateiobjekt. Diese Methode speichert dieses Dateiobjekt in der Instanzvariablen self.out_file. So merkt sie sich, wohin wir die Ausgabe umleiten möchten.
  2. __enter__:
    • Zuerst speichert es die aktuelle sys.stdout. Dies ist wichtig, weil wir sie später wiederherstellen müssen.
    • Dann ersetzt es die aktuelle sys.stdout durch unser Dateiobjekt. Ab diesem Zeitpunkt geht jede Ausgabe, die normalerweise an die Konsole gehen würde, stattdessen in die Datei.
    • Schließlich gibt es das Dateiobjekt zurück. Dies ist nützlich, weil wir möglicherweise das Dateiobjekt innerhalb des with-Blocks verwenden möchten.
  3. __exit__:
    • Diese Methode stellt die ursprüngliche sys.stdout wieder her. So geht die Ausgabe nach Abschluss des with-Blocks wieder normalerweise an die Konsole.
    • Sie nimmt drei Parameter entgegen: Ausnahmetyp (ty), Ausnahme-Wert (val) und Stapelüberwachung (tb). Diese Parameter sind für das Kontext-Manager-Protokoll erforderlich. Sie werden verwendet, um alle Ausnahmen zu behandeln, die innerhalb des with-Blocks auftreten können.

Jetzt testen wir unseren Kontext-Manager. Wir werden ihn verwenden, um die Ausgabe einer Tabelle in eine Datei umzuleiten. Zuerst starten Sie die Python-Shell:

python3

Führen Sie dann den folgenden Python-Code in der Shell aus:

>>> import stock, reader, tableformat
>>> from redirect import redirect_stdout
>>> portfolio = reader.read_csv_as_instances('portfolio.csv', stock.Stock)
>>> formatter = tableformat.create_formatter('text')
>>> with redirect_stdout(open('out.txt', 'w')) as file:
...     tableformat.print_table(portfolio, ['name','shares','price'], formatter)
...     file.close()
...
>>> ## Let's check the content of the output file
>>> print(open('out.txt').read())
      name     shares      price
---------- ---------- ----------
        AA        100       32.2
       IBM         50       91.1
       CAT        150      83.44
      MSFT        200      51.23
        GE         95      40.37
      MSFT         50       65.1
       IBM        100      70.44

Toll! Unser Kontext-Manager hat wie erwartet funktioniert. Er hat die Tabellenausgabe erfolgreich in die Datei out.txt umgeleitet.

Kontext-Manager sind eine sehr mächtige Funktion in Python. Sie helfen Ihnen, Ressourcen richtig zu verwalten. Hier sind einige häufige Anwendungsfälle für Kontext-Manager:

  • Dateioperationen: Wenn Sie eine Datei öffnen, kann ein Kontext-Manager sicherstellen, dass die Datei ordnungsgemäß geschlossen wird, auch wenn ein Fehler auftritt.
  • Datenbankverbindungen: Er kann sicherstellen, dass die Datenbankverbindung geschlossen wird, nachdem Sie sie verwendet haben.
  • Sperren in Thread-Programmen: Kontext-Manager können Ressourcen auf sichere Weise sperren und entsperren.
  • Temporäres Ändern von Umgebungs-Einstellungen: Sie können einige Einstellungen für einen Codeblock ändern und sie dann automatisch wiederherstellen.

Dieses Muster ist sehr wichtig, weil es sicherstellt, dass Ressourcen ordnungsgemäß aufgeräumt werden, auch wenn eine Ausnahme innerhalb des with-Blocks auftritt.

Wenn Sie mit dem Testen fertig sind, können Sie die Python-Shell beenden:

>>> exit()

Zusammenfassung

In diesem Lab haben Sie gelernt, wie Sie die String - Darstellung von Objekten mit der __repr__ - Methode anpassen, Objekte mit der __eq__ - Methode vergleichbar machen und einen Kontext - Manager mit den __enter__ - und __exit__ - Methoden erstellen können. Diese speziellen "Dunder - Methoden" sind der Grundstein für die objektorientierten Features von Python.

Das Implementieren dieser Methoden in Ihren Klassen ermöglicht es Ihren Objekten, sich wie eingebaute Typen zu verhalten und nahtlos in die Sprachfeatures von Python zu integrieren. Spezielle Methoden ermöglichen verschiedene Funktionen wie benutzerdefinierte String - Darstellungen, Objektvergleiche und Kontextverwaltung. Wenn Sie sich in Python weiterentwickeln, werden Sie noch mehr spezielle Methoden entdecken, um das leistungsstarke Objektmodell zu nutzen.