Einführung
In diesem Lab werden Sie lernen, wie Sie in Python einen neuen primitiven Datentyp erstellen und für ihn die wesentlichen Methoden implementieren. Sie werden auch ein Verständnis für Python's Objektprotokoll gewinnen. In den meisten Python - Programmen werden eingebaute primitive Datentypen wie int, float und str verwendet, um Daten darzustellen. Allerdings ermöglicht es Ihnen Python, benutzerdefinierte Datentypen zu erstellen, wie es in Modulen wie decimal und fractions in der Standardbibliothek zu sehen ist.
In diesem Lab werden Sie einen neuen primitiven Datentyp namens MutInt (veränderliche Ganzzahl) erstellen. Im Gegensatz zu Python's unveränderlichen Ganzzahlen kann MutInt nach der Erstellung modifiziert werden. Diese Übung wird die grundlegenden Prinzipien aufzeigen, die erforderlich sind, um einen voll funktionsfähigen primitiven Datentyp in Python zu erstellen.
Erstellen einer grundlegenden MutInt - Klasse
Beginnen wir damit, eine grundlegende Klasse für unseren veränderlichen Ganzzahltyp zu erstellen. In der Programmierung ist eine Klasse wie ein Bauplan für die Erstellung von Objekten. In diesem Schritt werden wir die Grundlage für unseren neuen primitiven Datentyp legen. Ein primitiver Datentyp ist ein grundlegender Datentyp, der von einer Programmiersprache bereitgestellt wird, und hier werden wir unseren eigenen benutzerdefinierten Datentyp erstellen.
Öffnen Sie die WebIDE und navigieren Sie zum Verzeichnis
/home/labex/project. Die WebIDE ist eine integrierte Entwicklungsumgebung, in der Sie Ihren Code schreiben, bearbeiten und ausführen können. Das Navigieren zu diesem Verzeichnis stellt sicher, dass alle Ihre Dateien an einem Ort organisiert sind und richtig miteinander interagieren können.Öffnen Sie die Datei
mutint.py, die für Sie im Einrichtungsschritt erstellt wurde. In dieser Datei wird die Definition unsererMutInt- Klasse gespeichert.Fügen Sie den folgenden Code hinzu, um eine grundlegende
MutInt- Klasse zu definieren:
## mutint.py
class MutInt:
"""
A mutable integer class that allows its value to be modified after creation.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialize with an integer value."""
self.value = value
Das __slots__ - Attribut wird verwendet, um die Attribute zu definieren, die diese Klasse haben kann. Attribute sind wie Variablen, die zu einem Objekt der Klasse gehören. Durch die Verwendung von __slots__ sagen wir Python, ein speicher - effizienteres Verfahren zur Speicherung von Attributen zu verwenden. In diesem Fall wird unsere MutInt - Klasse nur ein einzelnes Attribut namens value haben. Dies bedeutet, dass jedes Objekt der MutInt - Klasse nur ein Datenstück aufnehmen kann, nämlich den ganzzahligen Wert.
Die __init__ - Methode ist der Konstruktor für unsere Klasse. Ein Konstruktor ist eine spezielle Methode, die aufgerufen wird, wenn ein Objekt der Klasse erstellt wird. Sie nimmt einen Wertparameter entgegen und speichert ihn im value - Attribut der Instanz. Eine Instanz ist ein einzelnes Objekt, das aus dem Klassenbauplan erstellt wird.
Lassen Sie uns unsere Klasse testen, indem wir ein Python - Skript erstellen, um sie zu verwenden:
- Erstellen Sie eine neue Datei namens
test_mutint.pyim gleichen Verzeichnis:
## test_mutint.py
from mutint import MutInt
## Create a MutInt object
a = MutInt(3)
print(f"Created MutInt with value: {a.value}")
## Modify the value (demonstrating mutability)
a.value = 42
print(f"Modified value to: {a.value}")
## Try adding (this will fail)
try:
result = a + 10
print(f"Result of a + 10: {result}")
except TypeError as e:
print(f"Error when adding: {e}")
In diesem Testskript importieren wir zunächst die MutInt - Klasse aus der Datei mutint.py. Dann erstellen wir ein Objekt der MutInt - Klasse mit einem Anfangswert von 3. Wir geben den Anfangswert aus, ändern ihn dann auf 42 und geben den neuen Wert aus. Schließlich versuchen wir, 10 zum MutInt - Objekt hinzuzufügen, was zu einem Fehler führt, da unsere Klasse die Additionsoperation noch nicht unterstützt.
- Führen Sie das Testskript aus, indem Sie den folgenden Befehl im Terminal ausführen:
python3 /home/labex/project/test_mutint.py
Das Terminal ist eine Befehlszeilenoberfläche, in der Sie verschiedene Befehle ausführen können, um mit Ihrem System und Ihrem Code zu interagieren. Das Ausführen dieses Befehls führt das test_mutint.py - Skript aus.
Sie sollten eine Ausgabe ähnlich der folgenden sehen:
Created MutInt with value: 3
Modified value to: 42
Error when adding: unsupported operand type(s) for +: 'MutInt' and 'int'
Unsere MutInt - Klasse speichert und aktualisiert erfolgreich einen Wert. Allerdings hat sie mehrere Einschränkungen:
- Sie wird nicht schön angezeigt, wenn sie ausgegeben wird.
- Sie unterstützt keine mathematischen Operationen wie Addition.
- Sie unterstützt keine Vergleiche.
- Sie unterstützt keine Typkonvertierungen.
In den nächsten Schritten werden wir diese Einschränkungen nacheinander beseitigen, damit unsere MutInt - Klasse sich mehr wie ein echter primitiver Datentyp verhält.
Verbesserung der String - Darstellung
Wenn Sie in Python ein MutInt - Objekt ausgeben, sehen Sie eine Ausgabe wie <__main__.MutInt object at 0x...>. Diese Ausgabe ist nicht sehr hilfreich, da sie Ihnen den tatsächlichen Wert des MutInt - Objekts nicht mitteilt. Um es einfacher zu machen, zu verstehen, was das Objekt repräsentiert, werden wir spezielle Methoden für die String - Darstellung implementieren.
- Öffnen Sie
mutint.pyin der WebIDE und aktualisieren Sie es mit dem folgenden Code:
## mutint.py
class MutInt:
"""
A mutable integer class that allows its value to be modified after creation.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialize with an integer value."""
self.value = value
def __str__(self):
"""Return a string representation for printing."""
return str(self.value)
def __repr__(self):
"""Return a developer-friendly string representation."""
return f'MutInt({self.value!r})'
def __format__(self, fmt):
"""Support string formatting with format specifications."""
return format(self.value, fmt)
Wir haben drei wichtige Methoden zur MutInt - Klasse hinzugefügt:
__str__(): Diese Methode wird aufgerufen, wenn Sie diestr()- Funktion auf das Objekt anwenden oder das Objekt direkt ausgeben. Sie sollte eine menschenlesbare Zeichenkette zurückgeben.__repr__(): Diese Methode liefert die "offizielle" String - Darstellung des Objekts. Sie wird hauptsächlich zum Debugging verwendet und sollte idealerweise eine Zeichenkette zurückgeben, die, wenn sie an dieeval()- Funktion übergeben wird, das Objekt neu erstellen würde.__format__(): Diese Methode ermöglicht es Ihnen, Python's Zeichenkettenformatierungssystem mit IhrenMutInt- Objekten zu verwenden. Sie können Formatangaben wie Auffüllung und Zahlenformatierung nutzen.
- Erstellen Sie eine neue Testdatei namens
test_string_repr.py, um diese neuen Methoden zu testen:
## test_string_repr.py
from mutint import MutInt
## Create a MutInt object
a = MutInt(3)
## Test string representation
print(f"str(a): {str(a)}")
print(f"repr(a): {repr(a)}")
## Test direct printing
print(f"Print a: {a}")
## Test string formatting
print(f"Formatted with padding: '{a:*^10}'")
print(f"Formatted as decimal: '{a:d}'")
## Test mutability
a.value = 42
print(f"After changing value, repr(a): {repr(a)}")
In dieser Testdatei importieren wir zunächst die MutInt - Klasse. Dann erstellen wir ein MutInt - Objekt mit dem Wert 3. Wir testen die __str__() - und __repr__() - Methoden, indem wir die str() - und repr() - Funktionen verwenden. Wir testen auch die direkte Ausgabe, die Zeichenkettenformatierung und die Veränderlichkeit des MutInt - Objekts.
- Führen Sie das Testskript aus:
python3 /home/labex/project/test_string_repr.py
Wenn Sie diesen Befehl ausführen, wird Python das test_string_repr.py - Skript ausführen. Sie sollten eine Ausgabe ähnlich der folgenden sehen:
str(a): 3
repr(a): MutInt(3)
Print a: 3
Formatted with padding: '****3*****'
Formatted as decimal: '3'
After changing value, repr(a): MutInt(42)
Jetzt werden unsere MutInt - Objekte schön angezeigt. Die String - Darstellung zeigt den zugrunde liegenden Wert, und wir können die Zeichenkettenformatierung genauso wie bei normalen Ganzzahlen verwenden.
Der Unterschied zwischen __str__() und __repr__() besteht darin, dass __str__() dazu dient, eine menschenfreundliche Ausgabe zu erzeugen, während __repr__() idealerweise eine Zeichenkette erzeugen sollte, die, wenn sie an eval() übergeben wird, das Objekt neu erstellen würde. Deshalb haben wir den Klassennamen in der __repr__() - Methode enthalten.
Die __format__() - Methode ermöglicht es unserem Objekt, mit Python's Formatierungssystem zu arbeiten, sodass wir Formatangaben wie Auffüllung und Zahlenformatierung verwenden können.
Hinzufügen von mathematischen Operationen
Derzeit unterstützt unsere MutInt - Klasse keine mathematischen Operationen wie Addition. In Python müssen wir für eine benutzerdefinierte Klasse spezielle Methoden implementieren, um solche Operationen zu ermöglichen. Diese speziellen Methoden werden auch als "Magie - Methoden" oder "Dunder - Methoden" bezeichnet, da sie von doppelten Unterstrichen umgeben sind. Fügen wir die Funktionalität für die Addition hinzu, indem wir die relevanten speziellen Methoden für arithmetische Operationen implementieren.
- Öffnen Sie
mutint.pyin der WebIDE und aktualisieren Sie es mit dem folgenden Code:
## mutint.py
class MutInt:
"""
A mutable integer class that allows its value to be modified after creation.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialize with an integer value."""
self.value = value
def __str__(self):
"""Return a string representation for printing."""
return str(self.value)
def __repr__(self):
"""Return a developer-friendly string representation."""
return f'MutInt({self.value!r})'
def __format__(self, fmt):
"""Support string formatting with format specifications."""
return format(self.value, fmt)
def __add__(self, other):
"""Handle addition: self + other."""
if isinstance(other, MutInt):
return MutInt(self.value + other.value)
elif isinstance(other, int):
return MutInt(self.value + other)
else:
return NotImplemented
def __radd__(self, other):
"""Handle reversed addition: other + self."""
## For commutative operations like +, we can reuse __add__
return self.__add__(other)
def __iadd__(self, other):
"""Handle in-place addition: self += other."""
if isinstance(other, MutInt):
self.value += other.value
return self
elif isinstance(other, int):
self.value += other
return self
else:
return NotImplemented
Wir haben drei neue Methoden zur MutInt - Klasse hinzugefügt:
__add__(): Diese Methode wird aufgerufen, wenn der+- Operator mit unseremMutInt- Objekt auf der linken Seite verwendet wird. Innerhalb dieser Methode überprüfen wir zunächst, ob derother- Operand eine Instanz vonMutIntoder einintist. Wenn dies der Fall ist, führen wir die Addition aus und geben ein neuesMutInt- Objekt mit dem Ergebnis zurück. Wenn derother- Operand etwas anderes ist, geben wirNotImplementedzurück. Dies sagt Python, andere Methoden zu versuchen oder einenTypeErrorauszulösen.__radd__(): Diese Methode wird aufgerufen, wenn der+- Operator mit unseremMutInt- Objekt auf der rechten Seite verwendet wird. Da die Addition eine kommutative Operation ist (d. h.,a + bist dasselbe wieb + a), können wir einfach die__add__- Methode wiederverwenden.__iadd__(): Diese Methode wird aufgerufen, wenn der+=- Operator auf unseremMutInt- Objekt verwendet wird. Anstatt ein neues Objekt zu erstellen, modifiziert sie das vorhandeneMutInt- Objekt und gibt es zurück.
- Erstellen Sie eine neue Testdatei namens
test_math_ops.py, um diese neuen Methoden zu testen:
## test_math_ops.py
from mutint import MutInt
## Create MutInt objects
a = MutInt(3)
b = MutInt(5)
## Test regular addition
c = a + b
print(f"a + b = {c}")
## Test addition with int
d = a + 10
print(f"a + 10 = {d}")
## Test reversed addition
e = 7 + a
print(f"7 + a = {e}")
## Test in-place addition
print(f"Before a += 5: a = {a}")
a += 5
print(f"After a += 5: a = {a}")
## Test in-place addition with reference sharing
f = a ## f and a point to the same object
print(f"Before a += 10: a = {a}, f = {f}")
a += 10
print(f"After a += 10: a = {a}, f = {f}")
## Test unsupported operation
try:
result = a + 3.5 ## Adding a float is not supported
print(f"a + 3.5 = {result}")
except TypeError as e:
print(f"Error when adding float: {e}")
In dieser Testdatei importieren wir zunächst die MutInt - Klasse. Dann erstellen wir einige MutInt - Objekte und führen verschiedene Arten von Additionsoperationen aus. Wir testen auch die in - place - Addition und den Fall, in dem eine nicht unterstützte Operation (Addition einer Fließkommazahl) versucht wird.
- Führen Sie das Testskript aus:
python3 /home/labex/project/test_math_ops.py
Sie sollten eine Ausgabe ähnlich der folgenden sehen:
a + b = MutInt(8)
a + 10 = MutInt(13)
7 + a = MutInt(10)
Before a += 5: a = MutInt(3)
After a += 5: a = MutInt(8)
Before a += 10: a = MutInt(8), f = MutInt(8)
After a += 10: a = MutInt(18), f = MutInt(18)
Error when adding float: unsupported operand type(s) for +: 'MutInt' and 'float'
Jetzt unterstützt unsere MutInt - Klasse grundlegende Additionsoperationen. Beachten Sie, dass sowohl a als auch f aktualisiert wurden, als wir += verwendeten. Dies zeigt, dass a += 10 das vorhandene Objekt modifiziert hat, anstatt ein neues zu erstellen.
Dieses Verhalten bei veränderlichen Objekten ähnelt Python's eingebauten veränderlichen Typen wie Listen. Beispielsweise:
a = [1, 2, 3]
b = a
a += [4, 5] ## Both a and b are updated
Im Gegensatz dazu erstellt += für unveränderliche Typen wie Tupel ein neues Objekt:
c = (1, 2, 3)
d = c
c += (4, 5) ## c is a new object, d still points to the old one
Implementierung von Vergleichsoperationen
Derzeit können unsere MutInt - Objekte nicht miteinander oder mit normalen Ganzzahlen verglichen werden. In Python sind Vergleichsoperationen wie ==, <, <=, >, >= sehr nützlich, wenn man mit Objekten arbeitet. Sie ermöglichen es uns, Beziehungen zwischen verschiedenen Objekten festzustellen, was in vielen Programmier-Szenarien wie Sortieren, Filtern und bedingten Anweisungen von entscheidender Bedeutung ist. Fügen wir daher Vergleichsfunktionen zu unserer MutInt - Klasse hinzu, indem wir die speziellen Methoden für Vergleichsoperationen implementieren.
- Öffnen Sie
mutint.pyin der WebIDE und aktualisieren Sie es mit dem folgenden Code:
## mutint.py
from functools import total_ordering
@total_ordering
class MutInt:
"""
A mutable integer class that allows its value to be modified after creation.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialize with an integer value."""
self.value = value
def __str__(self):
"""Return a string representation for printing."""
return str(self.value)
def __repr__(self):
"""Return a developer-friendly string representation."""
return f'MutInt({self.value!r})'
def __format__(self, fmt):
"""Support string formatting with format specifications."""
return format(self.value, fmt)
def __add__(self, other):
"""Handle addition: self + other."""
if isinstance(other, MutInt):
return MutInt(self.value + other.value)
elif isinstance(other, int):
return MutInt(self.value + other)
else:
return NotImplemented
def __radd__(self, other):
"""Handle reversed addition: other + self."""
return self.__add__(other)
def __iadd__(self, other):
"""Handle in-place addition: self += other."""
if isinstance(other, MutInt):
self.value += other.value
return self
elif isinstance(other, int):
self.value += other
return self
else:
return NotImplemented
def __eq__(self, other):
"""Handle equality comparison: self == other."""
if isinstance(other, MutInt):
return self.value == other.value
elif isinstance(other, int):
return self.value == other
else:
return NotImplemented
def __lt__(self, other):
"""Handle less-than comparison: self < other."""
if isinstance(other, MutInt):
return self.value < other.value
elif isinstance(other, int):
return self.value < other
else:
return NotImplemented
Wir haben mehrere wichtige Verbesserungen vorgenommen:
Importieren und verwenden Sie den
@total_ordering- Dekorator aus demfunctools- Modul. Der@total_ordering- Dekorator ist ein leistungsstarkes Werkzeug in Python. Er hilft uns, viel Zeit und Mühe zu sparen, wenn wir Vergleichsmethoden für eine Klasse implementieren. Anstatt alle sechs Vergleichsmethoden (__eq__,__ne__,__lt__,__le__,__gt__,__ge__) manuell zu definieren, müssen wir nur__eq__und eine andere Vergleichsmethode (in unserem Fall__lt__) definieren. Der Dekorator generiert dann automatisch die verbleibenden vier Vergleichsmethoden für uns.Fügen Sie die
__eq__()- Methode hinzu, um Gleichheitsvergleiche (==) zu behandeln. Diese Methode wird verwendet, um zu prüfen, ob zweiMutInt- Objekte oder einMutInt- Objekt und eine Ganzzahl denselben Wert haben.Fügen Sie die
__lt__()- Methode hinzu, um "kleiner - als" - Vergleiche (<) zu behandeln. Diese Methode wird verwendet, um festzustellen, ob einMutInt- Objekt oder einMutInt- Objekt im Vergleich zu einer Ganzzahl einen kleineren Wert hat.Erstellen Sie eine neue Testdatei namens
test_comparisons.py, um diese neuen Methoden zu testen:
## test_comparisons.py
from mutint import MutInt
## Create MutInt objects
a = MutInt(3)
b = MutInt(3)
c = MutInt(5)
## Test equality
print(f"a == b: {a == b}") ## Should be True (same value)
print(f"a == c: {a == c}") ## Should be False (different values)
print(f"a == 3: {a == 3}") ## Should be True (comparing with int)
print(f"a == 5: {a == 5}") ## Should be False (different values)
## Test less than
print(f"a < c: {a < c}") ## Should be True (3 < 5)
print(f"c < a: {c < a}") ## Should be False (5 is not < 3)
print(f"a < 4: {a < 4}") ## Should be True (3 < 4)
## Test other comparisons (provided by @total_ordering)
print(f"a <= b: {a <= b}") ## Should be True (3 <= 3)
print(f"a > c: {a > c}") ## Should be False (3 is not > 5)
print(f"c >= a: {c >= a}") ## Should be True (5 >= 3)
## Test with a different type
print(f"a == '3': {a == '3'}") ## Should be False (different types)
In dieser Testdatei erstellen wir mehrere MutInt - Objekte und führen verschiedene Vergleichsoperationen auf ihnen aus. Wir vergleichen auch MutInt - Objekte mit normalen Ganzzahlen und einem anderen Typ (in diesem Fall einer Zeichenkette). Durch das Ausführen dieser Tests können wir überprüfen, ob unsere Vergleichsmethoden wie erwartet funktionieren.
- Führen Sie das Testskript aus:
python3 /home/labex/project/test_comparisons.py
Sie sollten eine Ausgabe ähnlich der folgenden sehen:
a == b: True
a == c: False
a == 3: True
a == 5: False
a < c: True
c < a: False
a < 4: True
a <= b: True
a > c: False
c >= a: True
a == '3': False
Jetzt unterstützt unsere MutInt - Klasse alle Vergleichsoperationen.
Der @total_ordering - Dekorator ist besonders nützlich, da er uns erspart, alle sechs Vergleichsmethoden manuell zu implementieren. Indem wir nur __eq__ und __lt__ bereitstellen, kann Python die anderen vier Vergleichsmethoden automatisch ableiten.
Beim Implementieren benutzerdefinierter Klassen ist es im Allgemeinen eine gute Praxis, dass sie sowohl mit Objekten desselben Typs als auch mit eingebauten Typen funktionieren, wenn es sinnvoll ist. Deshalb behandeln unsere Vergleichsmethoden sowohl MutInt - Objekte als auch normale Ganzzahlen. Auf diese Weise kann unsere MutInt - Klasse flexibler in verschiedenen Programmier-Szenarien verwendet werden.
Hinzufügen von Typkonvertierungen
Unsere MutInt-Klasse unterstützt derzeit Additions- und Vergleichsoperationen. Sie funktioniert jedoch nicht mit den in Python integrierten Konvertierungsfunktionen wie int() und float(). Diese Konvertierungsfunktionen sind in Python sehr nützlich. Wenn Sie beispielsweise einen Wert in eine Ganzzahl (Integer) oder eine Gleitkommazahl (Floating-Point Number) für verschiedene Berechnungen oder Operationen konvertieren möchten, verlassen Sie sich auf diese Funktionen. Fügen wir also unserer MutInt-Klasse die Fähigkeit hinzu, mit ihnen zu arbeiten.
- Öffnen Sie
mutint.pyin der WebIDE und aktualisieren Sie es mit dem folgenden Code:
## mutint.py
from functools import total_ordering
@total_ordering
class MutInt:
"""
Eine veränderliche (mutable) Integer-Klasse, die es ermöglicht, ihren Wert nach der Erstellung zu ändern.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialisierung mit einem Integer-Wert."""
self.value = value
def __str__(self):
"""Gibt eine String-Repräsentation für die Ausgabe zurück."""
return str(self.value)
def __repr__(self):
"""Gibt eine entwicklerfreundliche String-Repräsentation zurück."""
return f'MutInt({self.value!r})'
def __format__(self, fmt):
"""Unterstützt String-Formatierung mit Format-Spezifikationen."""
return format(self.value, fmt)
def __add__(self, other):
"""Behandelt Addition: self + other."""
if isinstance(other, MutInt):
return MutInt(self.value + other.value)
elif isinstance(other, int):
return MutInt(self.value + other)
else:
return NotImplemented
def __radd__(self, other):
"""Behandelt umgekehrte Addition: other + self."""
return self.__add__(other)
def __iadd__(self, other):
"""Behandelt In-Place-Addition: self += other."""
if isinstance(other, MutInt):
self.value += other.value
return self
elif isinstance(other, int):
self.value += other
return self
else:
return NotImplemented
def __eq__(self, other):
"""Behandelt Gleichheitsvergleich: self == other."""
if isinstance(other, MutInt):
return self.value == other.value
elif isinstance(other, int):
return self.value == other
else:
return NotImplemented
def __lt__(self, other):
"""Behandelt Kleiner-als-Vergleich: self < other."""
if isinstance(other, MutInt):
return self.value < other.value
elif isinstance(other, int):
return self.value < other
else:
return NotImplemented
def __int__(self):
"""Konvertiert zu int (Ganzzahl)."""
return self.value
def __float__(self):
"""Konvertiert zu float (Gleitkommazahl)."""
return float(self.value)
__index__ = __int__ ## Unterstützt Array-Indizierung und andere Operationen, die einen Index erfordern
def __lshift__(self, other):
"""Behandelt Linksschieben: self << other."""
if isinstance(other, MutInt):
return MutInt(self.value << other.value)
elif isinstance(other, int):
return MutInt(self.value << other)
else:
return NotImplemented
def __rlshift__(self, other):
"""Behandelt umgekehrtes Linksschieben: other << self."""
if isinstance(other, int):
return MutInt(other << self.value)
else:
return NotImplemented
Wir haben der MutInt-Klasse drei neue Methoden hinzugefügt:
__int__(): Diese Methode wird aufgerufen, wenn Sie die Funktionint()auf ein Objekt unsererMutInt-Klasse anwenden. Wenn Sie beispielsweise einMutInt-Objektahaben undint(a)schreiben, ruft Python die Methode__int__()des Objektsaauf.__float__(): Ebenso wird diese Methode aufgerufen, wenn Sie die Funktionfloat()auf unserMutInt-Objekt anwenden.__index__(): Diese Methode wird für Operationen verwendet, die speziell einen Integer-Index (Ganzzahlindex) erfordern. Wenn Sie beispielsweise auf ein Element in einer Liste über einen Index zugreifen oder Bitlängenoperationen durchführen möchten, benötigt Python einen Integer-Index.__lshift__(): Diese Methode behandelt Linksschiebeoperationen, wenn sich dasMutInt-Objekt auf der linken Seite des Operators<<befindet.__rlshift__(): Diese Methode behandelt Linksschiebeoperationen, wenn sich dasMutInt-Objekt auf der rechten Seite des Operators<<befindet.
Die Methode __index__ ist entscheidend für Operationen, die einen Integer-Index erfordern, wie z. B. Listenindizierung, Slicing und Bitlängenoperationen. In unserer einfachen Implementierung setzen wir sie auf dasselbe wie __int__, da der Wert unseres MutInt-Objekts direkt als Integer-Index verwendet werden kann.
Die Methoden __lshift__ und __rlshift__ sind unerlässlich, um bitweise Linksschiebeoperationen zu unterstützen. Sie ermöglichen es unseren MutInt-Objekten, an bitweisen Operationen teilzunehmen, was eine häufige Anforderung für integerähnliche Typen ist.
- Erstellen Sie eine neue Testdatei namens
test_conversions.py, um diese neuen Methoden zu testen:
## test_conversions.py
from mutint import MutInt
## Erstellt ein MutInt-Objekt
a = MutInt(3)
## Testet Konvertierungen
print(f"int(a): {int(a)}")
print(f"float(a): {float(a)}")
## Testet die Verwendung als Index
names = ['Dave', 'Guido', 'Paula', 'Thomas', 'Lewis']
print(f"names[a]: {names[a]}")
## Testet die Verwendung in Bitoperationen (erfordert __index__)
print(f"1 << a: {1 << a}") ## Verschiebt um 3 Stellen nach links
## Testet hex/oct/bin-Funktionen (erfordert __index__)
print(f"hex(a): {hex(a)}")
print(f"oct(a): {oct(a)}")
print(f"bin(a): {bin(a)}")
## Modifiziert und testet erneut
a.value = 4
print(f"\nNachdem der Wert auf 4 geändert wurde:")
print(f"int(a): {int(a)}")
print(f"names[a]: {names[a]}")
- Führen Sie das Testskript aus:
python3 /home/labex/project/test_conversions.py
Sie sollten eine ähnliche Ausgabe wie diese sehen:
int(a): 3
float(a): 3.0
names[a]: Thomas
1 << a: 8
hex(a): 0x3
oct(a): 0o3
bin(a): 0b11
Nachdem der Wert auf 4 geändert wurde:
int(a): 4
names[a]: Lewis
Jetzt kann unsere MutInt-Klasse in Standard-Python-Typen konvertiert und in Operationen verwendet werden, die einen Integer-Index erfordern.
Die Methode __index__ ist besonders wichtig. Sie wurde in Python eingeführt, um die Verwendung von Objekten in Situationen zu ermöglichen, in denen ein Integer-Index erforderlich ist, wie z. B. Listenindizierung, bitweise Operationen und verschiedene Funktionen wie hex(), oct() und bin().
Mit diesen Ergänzungen ist unsere MutInt-Klasse nun ein recht vollständiger primitiver Typ. Sie kann in den meisten Kontexten verwendet werden, in denen eine reguläre Ganzzahl (Integer) verwendet würde, mit dem zusätzlichen Vorteil, dass sie veränderlich (mutable) ist.
Vollständige MutInt-Implementierung
Hier ist unsere vollständige MutInt-Implementierung mit allen Funktionen, die wir hinzugefügt haben:
## mutint.py
from functools import total_ordering
@total_ordering
class MutInt:
"""
Eine veränderliche (mutable) Integer-Klasse, die es ermöglicht, ihren Wert nach der Erstellung zu ändern.
"""
__slots__ = ['value']
def __init__(self, value):
"""Initialisierung mit einem Integer-Wert."""
self.value = value
def __str__(self):
"""Gibt eine String-Repräsentation für die Ausgabe zurück."""
return str(self.value)
def __repr__(self):
"""Gibt eine entwicklerfreundliche String-Repräsentation zurück."""
return f'MutInt({self.value!r})'
def __format__(self, fmt):
"""Unterstützt String-Formatierung mit Format-Spezifikationen."""
return format(self.value, fmt)
def __add__(self, other):
"""Behandelt Addition: self + other."""
if isinstance(other, MutInt):
return MutInt(self.value + other.value)
elif isinstance(other, int):
return MutInt(self.value + other)
else:
return NotImplemented
def __radd__(self, other):
"""Behandelt umgekehrte Addition: other + self."""
return self.__add__(other)
def __iadd__(self, other):
"""Behandelt In-Place-Addition: self += other."""
if isinstance(other, MutInt):
self.value += other.value
return self
elif isinstance(other, int):
self.value += other
return self
else:
return NotImplemented
def __eq__(self, other):
"""Behandelt Gleichheitsvergleich: self == other."""
if isinstance(other, MutInt):
return self.value == other.value
elif isinstance(other, int):
return self.value == other
else:
return NotImplemented
def __lt__(self, other):
"""Behandelt Kleiner-als-Vergleich: self < other."""
if isinstance(other, MutInt):
return self.value < other.value
elif isinstance(other, int):
return self.value < other
else:
return NotImplemented
def __int__(self):
"""Konvertiert zu int (Ganzzahl)."""
return self.value
def __float__(self):
"""Konvertiert zu float (Gleitkommazahl)."""
return float(self.value)
__index__ = __int__ ## Unterstützt Array-Indizierung und andere Operationen, die einen Index erfordern
def __lshift__(self, other):
"""Behandelt Linksschieben: self << other."""
if isinstance(other, MutInt):
return MutInt(self.value << other.value)
elif isinstance(other, int):
return MutInt(self.value << other)
else:
return NotImplemented
def __rlshift__(self, other):
"""Behandelt umgekehrtes Linksschieben: other << self."""
if isinstance(other, int):
return MutInt(other << self.value)
else:
return NotImplemented
Diese Implementierung deckt die wichtigsten Aspekte der Erstellung eines neuen primitiven Typs in Python ab. Um sie noch vollständiger zu machen, könnten Sie zusätzliche Methoden für andere Operationen wie Subtraktion, Multiplikation, Division usw. implementieren.
Zusammenfassung
In diesem Lab haben Sie gelernt, wie Sie in Python Ihren eigenen primitiven Typ erstellen können. Insbesondere haben Sie gelernt, eine veränderliche Ganzzahlklasse ähnlich den eingebauten Typen zu erstellen, spezielle Methoden für die Objektanzeige zu implementieren, die Unterstützung für mathematische und Vergleichsoperationen hinzuzufügen und Typkonvertierungen für verschiedene Python - Kontexte zu ermöglichen.
Diese Konzepte sind für das Verständnis des Python - Objektmodells unerlässlich und können verwendet werden, um benutzerdefinierte Typen zu erstellen, die gut mit eingebauten Operationen integriert sind. Um Ihr Wissen zu erweitern, sollten Sie überlegen, weitere mathematische Operationen zu implementieren, die Unterstützung für andere eingebaute Funktionen hinzuzufügen und komplexe Typen wie benutzerdefinierte Sammlungen zu erkunden. Benutzerdefinierte Typen in Python sind ein leistungsstarkes Werkzeug, um die Sprache für Ihre Bedürfnisse zu erweitern.