Einführung
Die objektorientierten Programmierfunktionen (OOP) von Python bieten leistungsstarke Werkzeuge zur Erstellung ausdrucksstarker und angepasster Objekte. Unter diesen spielen die speziellen Methoden __init__, __str__ und __repr__ eine entscheidende Rolle bei der Definition des Verhaltens und der Darstellung Ihrer Python-Objekte.
In diesem Tutorial werden wir diese speziellen Methoden untersuchen, ihre Zwecke verstehen und lernen, wie man sie effektiv in Ihren Python-Klassen implementiert. Am Ende dieses Labors werden Sie in der Lage sein, intuitivere und benutzerfreundlichere Python-Objekte zu erstellen.
Erstellen von Klassen mit der __init__-Methode
Die erste spezielle Methode, die wir untersuchen werden, ist __init__, die aufgerufen wird, wenn ein Objekt erstellt wird. Mit dieser Methode können Sie die Attribute Ihres Objekts initialisieren.
Verständnis der __init__-Methode
Die __init__-Methode, auch als Konstruktor bekannt, wird automatisch aufgerufen, wenn Sie eine neue Instanz einer Klasse erstellen. Ihr Hauptzweck ist es, den Anfangszustand des Objekts einzurichten, indem Werte den Attributen zugewiesen werden.
Beginnen wir mit der Erstellung einer einfachen Python-Klasse mit der __init__-Methode:
Öffnen Sie das Terminal in Ihrer LabEx-Umgebung.
Navigieren Sie zum Projektverzeichnis:
cd ~/projectErstellen Sie eine neue Python-Datei namens
person.pymit dem Code-Editor:Fügen Sie im Editor den folgenden Code zu
person.pyhinzu:class Person: def __init__(self, name, age): self.name = name self.age = age ## Create a Person object person1 = Person("Alice", 30) ## Access the attributes print(f"Name: {person1.name}") print(f"Age: {person1.age}")Speichern Sie die Datei.
Führen Sie das Python-Skript aus:
python3 person.py
Sie sollten die folgende Ausgabe sehen:
Name: Alice
Age: 30
Erläuterung der __init__-Methode
Im obigen Code:
- Wir haben eine
Person-Klasse mit einer__init__-Methode definiert, die drei Parameter entgegennimmt:self,nameundage. - Der Parameter
selfbezieht sich auf die erstellte Instanz und wird automatisch übergeben, wenn ein Objekt erstellt wird. - Innerhalb der
__init__-Methode weisen wir die Werte vonnameundageden Attributen des Objekts mithilfe vonself.nameundself.agezu. - Wenn wir ein neues
Person-Objekt mitperson1 = Person("Alice", 30)erstellen, wird die__init__-Methode automatisch aufgerufen. - Wir können dann auf die Attribute mithilfe der Punktschreibweise zugreifen:
person1.nameundperson1.age.
Hinzufügen weiterer Funktionalität
Erweitern wir unsere Person-Klasse, indem wir eine Methode hinzufügen, um das Geburtsjahr basierend auf dem aktuellen Jahr und dem Alter der Person zu berechnen:
Öffnen Sie
person.pyim Editor.Aktualisieren Sie den Code, um eine Methode zur Berechnung des Geburtsjahres einzuschließen:
import datetime class Person: def __init__(self, name, age): self.name = name self.age = age def birth_year(self): current_year = datetime.datetime.now().year return current_year - self.age ## Create a Person object person1 = Person("Alice", 30) ## Access the attributes and call the method print(f"Name: {person1.name}") print(f"Age: {person1.age}") print(f"Birth Year: {person1.birth_year()}")Speichern Sie die Datei.
Führen Sie das Python-Skript aus:
python3 person.py
Die Ausgabe sollte nun das berechnete Geburtsjahr enthalten:
Name: Alice
Age: 30
Birth Year: 1993
(Das tatsächliche Geburtsjahr hängt vom aktuellen Jahr ab, wenn Sie das Skript ausführen)
Nun haben Sie eine Python-Klasse mit der __init__-Methode erstellt, um Objektattribute zu initialisieren, und eine Methode hinzugefügt, um Berechnungen basierend auf diesen Attributen durchzuführen.
Implementierung der __str__-Methode
Nachdem wir nun verstanden haben, wie man eine Klasse mit der __init__-Methode erstellt, wollen wir eine weitere spezielle Methode namens __str__ untersuchen. Mit dieser Methode können wir definieren, wie ein Objekt als Zeichenkette dargestellt werden soll.
Verständnis der __str__-Methode
Die __str__-Methode wird aufgerufen, wenn Sie die Funktion str() für ein Objekt verwenden oder wenn Sie ein Objekt mit der Funktion print() ausgeben. Sie sollte eine für Menschen lesbare Zeichenkettenrepräsentation des Objekts zurückgeben.
Aktualisieren wir unsere Person-Klasse, um eine __str__-Methode einzuschließen:
Öffnen Sie
person.pyim Editor.Aktualisieren Sie den Code, um eine
__str__-Methode einzuschließen:import datetime class Person: def __init__(self, name, age): self.name = name self.age = age def birth_year(self): current_year = datetime.datetime.now().year return current_year - self.age def __str__(self): return f"{self.name}, {self.age} years old" ## Create a Person object person1 = Person("Alice", 30) ## Access the attributes and call the method print(f"Name: {person1.name}") print(f"Age: {person1.age}") print(f"Birth Year: {person1.birth_year()}") ## Use the __str__ method implicitly print("\nString representation of the object:") print(person1) ## Use the __str__ method explicitly print("\nExplicit string conversion:") print(str(person1))Speichern Sie die Datei.
Führen Sie das Python-Skript aus:
python3 person.py
Sie sollten eine Ausgabe ähnlich dieser sehen:
Name: Alice
Age: 30
Birth Year: 1993
String representation of the object:
Alice, 30 years old
Explicit string conversion:
Alice, 30 years old
Wie __str__ funktioniert
Im obigen Code:
- Wir haben eine
__str__-Methode definiert, die eine formatierte Zeichenkette mit dem Namen und dem Alter der Person zurückgibt. - Wenn wir
print(person1)aufrufen, ruft Python automatisch die__str__-Methode auf, um zu bestimmen, was angezeigt werden soll. - Wir können ein Objekt auch explizit mit
str(person1)in eine Zeichenkette konvertieren, wodurch ebenfalls die__str__-Methode aufgerufen wird.
Was ohne __str__ passiert
Um die Bedeutung der __str__-Methode zu verstehen, wollen wir sehen, was passiert, wenn wir sie nicht definieren:
Erstellen Sie eine neue Datei namens
without_str.py:Fügen Sie den folgenden Code hinzu:
class SimpleClass: def __init__(self, value): self.value = value ## Create an object obj = SimpleClass(42) ## Print the object print(obj)Speichern Sie die Datei.
Führen Sie das Skript aus:
python3 without_str.py
Sie sollten eine Ausgabe wie diese sehen:
<__main__.SimpleClass object at 0x7f2d8c3e9d90>
Die Ausgabe ist nicht sehr informativ. Sie zeigt den Klassennamen und die Speicheradresse des Objekts, aber nicht seinen Inhalt. Aus diesem Grund ist die Implementierung einer ordnungsgemäßen __str__-Methode wichtig, um Ihre Objekte benutzerfreundlicher zu gestalten.
Praktische Übung
Erstellen wir eine neue Klasse namens Book mit einer __str__-Methode:
Erstellen Sie eine neue Datei namens
book.py:Fügen Sie den folgenden Code hinzu:
class Book: def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages def __str__(self): return f'"{self.title}" by {self.author} ({self.pages} pages)' ## Create book objects book1 = Book("The Great Gatsby", "F. Scott Fitzgerald", 180) book2 = Book("To Kill a Mockingbird", "Harper Lee", 281) ## Print the books print(book1) print(book2)Speichern Sie die Datei.
Führen Sie das Skript aus:
python3 book.py
Die Ausgabe sollte sein:
"The Great Gatsby" by F. Scott Fitzgerald (180 pages)
"To Kill a Mockingbird" by Harper Lee (281 pages)
Nun verstehen Sie, wie Sie die __str__-Methode verwenden können, um für Menschen lesbare Zeichenkettenrepräsentationen Ihrer Objekte zu erstellen.
Implementierung der __repr__-Methode
Zusätzlich zu __str__ bietet Python eine weitere spezielle Methode für die Zeichenkettenrepräsentation: __repr__. Während __str__ dazu dient, eine für Menschen lesbare Darstellung bereitzustellen, soll __repr__ eine eindeutige Darstellung eines Objekts liefern, die verwendet werden kann, um das Objekt, wenn möglich, neu zu erstellen.
Verständnis der __repr__-Methode
Die __repr__-Methode wird aufgerufen, wenn Sie die Funktion repr() für ein Objekt verwenden oder wenn Sie ein Objekt in einer interaktiven Sitzung anzeigen. Sie sollte eine Zeichenkette zurückgeben, die, wenn sie an eval() übergeben wird, ein äquivalentes Objekt erstellen würde (wenn möglich).
Aktualisieren wir unsere Book-Klasse, um eine __repr__-Methode einzuschließen:
Öffnen Sie
book.pyim Editor.Aktualisieren Sie den Code, um eine
__repr__-Methode einzuschließen:class Book: def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages def __str__(self): return f'"{self.title}" by {self.author} ({self.pages} pages)' def __repr__(self): return f'Book("{self.title}", "{self.author}", {self.pages})' ## Create book objects book1 = Book("The Great Gatsby", "F. Scott Fitzgerald", 180) book2 = Book("To Kill a Mockingbird", "Harper Lee", 281) ## Print the books (uses __str__) print("String representation (using __str__):") print(book1) print(book2) ## Get the representation (uses __repr__) print("\nRepresentation (using __repr__):") print(repr(book1)) print(repr(book2))Speichern Sie die Datei.
Führen Sie das Skript aus:
python3 book.py
Sie sollten eine Ausgabe wie diese sehen:
String representation (using __str__):
"The Great Gatsby" by F. Scott Fitzgerald (180 pages)
"To Kill a Mockingbird" by Harper Lee (281 pages)
Representation (using __repr__):
Book("The Great Gatsby", "F. Scott Fitzgerald", 180)
Book("To Kill a Mockingbird", "Harper Lee", 281)
Unterschiede zwischen __str__ und __repr__
Die Hauptunterschiede zwischen __str__ und __repr__ sind:
__str__ist für eine für Menschen lesbare Ausgabe gedacht, während__repr__für Entwickler und Debugging gedacht ist.- Wenn
__str__nicht definiert ist, aber__repr__definiert ist, verwendet Python__repr__als Fallback fürstr()oderprint(). __repr__sollte idealerweise eine Zeichenkette zurückgeben, die das Objekt neu erstellen kann, wenn es aneval()übergeben wird, obwohl dies nicht immer möglich oder notwendig ist.
Die eval()-Funktion mit __repr__
Wenn sie korrekt implementiert ist, kann die von __repr__ zurückgegebene Zeichenkette mit eval() verwendet werden, um das Objekt neu zu erstellen. Sehen wir uns das in Aktion an:
Erstellen Sie eine neue Datei namens
repr_eval.py:Fügen Sie den folgenden Code hinzu:
class Point: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"Point at ({self.x}, {self.y})" def __repr__(self): return f"Point({self.x}, {self.y})" ## Create a point p1 = Point(3, 4) ## Get the repr string repr_str = repr(p1) print(f"Representation: {repr_str}") ## Use eval to recreate the object p2 = eval(repr_str) print(f"Recreated object: {p2}") ## Verify they have the same values print(f"p1.x = {p1.x}, p1.y = {p1.y}") print(f"p2.x = {p2.x}, p2.y = {p2.y}")Speichern Sie die Datei.
Führen Sie das Skript aus:
python3 repr_eval.py
Sie sollten eine Ausgabe wie diese sehen:
Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4
Dies zeigt, dass wir das ursprüngliche Objekt mithilfe der von __repr__ zurückgegebenen Zeichenkette und der Funktion eval() neu erstellen können.
Wann welche Methode verwendet werden soll
- Verwenden Sie
__init__, um den Anfangszustand Ihrer Objekte einzurichten. - Verwenden Sie
__str__, um eine für Menschen lesbare Darstellung für Endbenutzer bereitzustellen. - Verwenden Sie
__repr__, um eine präzise, eindeutige Darstellung für Entwickler und Debugging bereitzustellen.
Praktische Übung: Ein vollständiges Beispiel
Lassen Sie uns alles zusammenfügen, indem wir eine Rectangle-Klasse mit allen drei speziellen Methoden erstellen:
Erstellen Sie eine neue Datei namens
rectangle.py:Fügen Sie den folgenden Code hinzu:
class Rectangle: def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height def perimeter(self): return 2 * (self.width + self.height) def __str__(self): return f"Rectangle with width {self.width} and height {self.height}" def __repr__(self): return f"Rectangle({self.width}, {self.height})" ## Create rectangles rect1 = Rectangle(5, 10) rect2 = Rectangle(3, 7) ## Display information about the rectangles print(f"Rectangle 1: {rect1}") print(f"Area: {rect1.area()}") print(f"Perimeter: {rect1.perimeter()}") print(f"Representation: {repr(rect1)}") print("\nRectangle 2: {0}".format(rect2)) print(f"Area: {rect2.area()}") print(f"Perimeter: {rect2.perimeter()}") print(f"Representation: {repr(rect2)}") ## Recreate a rectangle using eval rect3 = eval(repr(rect1)) print(f"\nRecreated rectangle: {rect3}") print(f"Is it the same area? {rect3.area() == rect1.area()}")Speichern Sie die Datei.
Führen Sie das Skript aus:
python3 rectangle.py
Sie sollten eine Ausgabe wie diese sehen:
Rectangle 1: Rectangle with width 5 and height 10
Area: 50
Perimeter: 30
Representation: Rectangle(5, 10)
Rectangle 2: Rectangle with width 3 and height 7
Area: 21
Perimeter: 20
Representation: Rectangle(3, 7)
Recreated rectangle: Rectangle with width 5 and height 10
Is it the same area? True
Dieses Beispiel zeigt, wie alle drei speziellen Methoden (__init__, __str__ und __repr__) zusammenarbeiten, um eine gut gestaltete Klasse zu erstellen.
Erstellung einer praktischen Anwendung
Nachdem wir nun die drei speziellen Methoden __init__, __str__ und __repr__ verstanden haben, wollen wir eine praktischere Anwendung erstellen, die ihre Verwendung in einem realen Szenario demonstriert. Wir erstellen ein einfaches Bankensystem mit einer BankAccount-Klasse.
Erstellung einer Bankkonto-Klasse
Erstellen Sie eine neue Datei namens
bank_account.py:Fügen Sie den folgenden Code hinzu:
class BankAccount: def __init__(self, account_number, owner_name, balance=0.0): self.account_number = account_number self.owner_name = owner_name self.balance = balance self.transactions = [] def deposit(self, amount): if amount <= 0: print("Deposit amount must be positive") return False self.balance += amount self.transactions.append(f"Deposit: +${amount:.2f}") return True def withdraw(self, amount): if amount <= 0: print("Withdrawal amount must be positive") return False if amount > self.balance: print("Insufficient funds") return False self.balance -= amount self.transactions.append(f"Withdrawal: -${amount:.2f}") return True def get_transaction_history(self): return self.transactions def __str__(self): return f"Account {self.account_number} | Owner: {self.owner_name} | Balance: ${self.balance:.2f}" def __repr__(self): return f'BankAccount("{self.account_number}", "{self.owner_name}", {self.balance})'Speichern Sie die Datei.
Testen der Bankkonto-Klasse
Testen wir nun unsere BankAccount-Klasse:
Erstellen Sie eine neue Datei namens
bank_test.py:Fügen Sie den folgenden Code hinzu:
from bank_account import BankAccount ## Create bank accounts account1 = BankAccount("12345", "John Doe", 1000.0) account2 = BankAccount("67890", "Jane Smith", 500.0) ## Display initial account information print("Initial Account Status:") print(account1) print(account2) ## Perform transactions print("\nPerforming transactions...") ## Deposit to account1 print("\nDepositing $250 to account1:") account1.deposit(250) print(account1) ## Withdraw from account2 print("\nWithdrawing $100 from account2:") account2.withdraw(100) print(account2) ## Try to withdraw too much from account2 print("\nTrying to withdraw $1000 from account2:") account2.withdraw(1000) print(account2) ## Try to deposit a negative amount to account1 print("\nTrying to deposit -$50 to account1:") account1.deposit(-50) print(account1) ## Display transaction history print("\nTransaction history for account1:") for transaction in account1.get_transaction_history(): print(f"- {transaction}") print("\nTransaction history for account2:") for transaction in account2.get_transaction_history(): print(f"- {transaction}") ## Recreate an account using repr print("\nRecreating account1 using repr:") account1_repr = repr(account1) print(f"Representation: {account1_repr}") recreated_account = eval(account1_repr) print(f"Recreated account: {recreated_account}")Speichern Sie die Datei.
Führen Sie das Skript aus:
python3 bank_test.py
Sie sollten eine Ausgabe ähnlich dieser sehen:
Initial Account Status:
Account 12345 | Owner: John Doe | Balance: $1000.00
Account 67890 | Owner: Jane Smith | Balance: $500.00
Performing transactions...
Depositing $250 to account1:
Account 12345 | Owner: John Doe | Balance: $1250.00
Withdrawing $100 from account2:
Account 67890 | Owner: Jane Smith | Balance: $400.00
Trying to withdraw $1000 from account2:
Insufficient funds
Account 67890 | Owner: Jane Smith | Balance: $400.00
Trying to deposit -$50 to account1:
Deposit amount must be positive
Account 12345 | Owner: John Doe | Balance: $1250.00
Transaction history for account1:
- Deposit: +$250.00
Transaction history for account2:
- Withdrawal: -$100.00
Recreating account1 using repr:
Representation: BankAccount("12345", "John Doe", 1250.0)
Recreated account: Account 12345 | Owner: John Doe | Balance: $1250.00
Verständnis der Implementierung
In dieser Banking-Anwendung:
Die
__init__-Methode initialisiert ein Bankkonto mit einer Kontonummer, dem Namen des Kontoinhabers und einem optionalen Anfangsguthaben. Sie erstellt auch eine leere Liste, um Transaktionen zu verfolgen.Die Methoden
depositundwithdrawverarbeiten Transaktionen und aktualisieren den Kontostand und den Transaktionsverlauf.Die
__str__-Methode liefert eine benutzerfreundliche Darstellung des Kontos, die die Kontonummer, den Namen des Kontoinhabers und den aktuellen Kontostand anzeigt.Die
__repr__-Methode liefert eine Zeichenkette, die das Kontoobjekt neu erstellen kann (beachten Sie jedoch, dass der Transaktionsverlauf in dieser Implementierung nicht erhalten bleibt).
Dieses Beispiel zeigt, wie diese speziellen Methoden in einer praktischen Anwendung verwendet werden können, um intuitivere und benutzerfreundlichere Objekte zu erstellen.
Erweiterung der Anwendung
Als letzte Übung erstellen wir ein einfaches Bankensystem, das mehrere Konten verwaltet:
Erstellen Sie eine neue Datei namens
banking_system.py:Fügen Sie den folgenden Code hinzu:
from bank_account import BankAccount class BankingSystem: def __init__(self, bank_name): self.bank_name = bank_name self.accounts = {} def create_account(self, account_number, owner_name, initial_balance=0.0): if account_number in self.accounts: print(f"Account {account_number} already exists") return None account = BankAccount(account_number, owner_name, initial_balance) self.accounts[account_number] = account return account def get_account(self, account_number): return self.accounts.get(account_number) def list_accounts(self): return list(self.accounts.values()) def __str__(self): return f"{self.bank_name} - Managing {len(self.accounts)} accounts" def __repr__(self): return f'BankingSystem("{self.bank_name}")' ## Create a banking system bank = BankingSystem("Python First Bank") print(bank) ## Create some accounts bank.create_account("A001", "John Doe", 1000) bank.create_account("A002", "Jane Smith", 500) bank.create_account("A003", "Bob Johnson", 250) ## List all accounts print("\nAll accounts:") for account in bank.list_accounts(): print(account) ## Make some transactions account = bank.get_account("A001") if account: print(f"\nBefore deposit: {account}") account.deposit(500) print(f"After deposit: {account}") account = bank.get_account("A002") if account: print(f"\nBefore withdrawal: {account}") account.withdraw(200) print(f"After withdrawal: {account}") ## Try to create an existing account print("\nTrying to create an existing account:") bank.create_account("A001", "Someone Else", 300) ## Final state of the banking system print(f"\n{bank}")Speichern Sie die Datei.
Führen Sie das Skript aus:
python3 banking_system.py
Sie sollten eine Ausgabe ähnlich der folgenden sehen:
Python First Bank - Managing 0 accounts
All accounts:
Account A001 | Owner: John Doe | Balance: $1000.00
Account A002 | Owner: Jane Smith | Balance: $500.00
Account A003 | Owner: Bob Johnson | Balance: $250.00
Before deposit: Account A001 | Owner: John Doe | Balance: $1000.00
After deposit: Account A001 | Owner: John Doe | Balance: $1500.00
Before withdrawal: Account A002 | Owner: Jane Smith | Balance: $500.00
After withdrawal: Account A002 | Owner: Jane Smith | Balance: $300.00
Trying to create an existing account:
Account A001 already exists
Python First Bank - Managing 3 accounts
Dieses Beispiel zeigt, wie die zuvor erstellte BankAccount-Klasse in einer umfassenderen Anwendung verwendet werden kann. Es zeigt, wie die speziellen Methoden __init__, __str__ und __repr__ eine solide Grundlage für die Erstellung intuitiver und benutzerfreundlicher Klassen bilden.
Zusammenfassung
In diesem Lab haben Sie drei wesentliche spezielle Methoden in Python kennengelernt, die Ihnen helfen, ausdrucksstärkere und benutzerfreundlichere Objekte zu erstellen:
__init__: Die Konstruktormethode, die die Attribute eines Objekts initialisiert, wenn es erstellt wird.__str__: Eine Methode, die eine für Menschen lesbare Zeichenkettenrepräsentation eines Objekts liefert, hauptsächlich für Endbenutzer.__repr__: Eine Methode, die eine eindeutige Zeichenkettenrepräsentation eines Objekts liefert, hauptsächlich für Entwickler und Debugging.
Sie haben diese Methoden in mehreren Beispielen implementiert, von einfachen Klassen wie Person und Book bis hin zu komplexeren Anwendungen wie einem Bankensystem. Sie haben auch die Unterschiede zwischen __str__ und __repr__ kennengelernt und wann welche Methode verwendet werden soll.
Durch die Beherrschung dieser speziellen Methoden können Sie intuitivere und angepasstere Python-Objekte erstellen, die sich nahtlos in die integrierten Funktionen der Sprache integrieren lassen. Dieses Wissen ist ein wesentlicher Bestandteil der objektorientierten Programmierung in Python und hilft Ihnen, saubereren, besser wartbaren Code zu schreiben.
Denken Sie auf Ihrer Python-Reise daran, dass es viele andere spezielle Methoden gibt, mit denen Sie das Verhalten Ihrer Objekte noch weiter anpassen können. Die Erforschung dieser Methoden gibt Ihnen noch mehr Kontrolle darüber, wie sich Ihre Objekte in verschiedenen Kontexten verhalten.



