Einen neuen primitiven Typ erstellen

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 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.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ControlFlowGroup(["Control Flow"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python/BasicConceptsGroup -.-> python/type_conversion("Type Conversion") python/ControlFlowGroup -.-> python/conditional_statements("Conditional Statements") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/constructor("Constructor") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") subgraph Lab Skills python/type_conversion -.-> lab-132443{{"Einen neuen primitiven Typ erstellen"}} python/conditional_statements -.-> lab-132443{{"Einen neuen primitiven Typ erstellen"}} python/function_definition -.-> lab-132443{{"Einen neuen primitiven Typ erstellen"}} python/classes_objects -.-> lab-132443{{"Einen neuen primitiven Typ erstellen"}} python/constructor -.-> lab-132443{{"Einen neuen primitiven Typ erstellen"}} python/decorators -.-> lab-132443{{"Einen neuen primitiven Typ erstellen"}} end

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.

  1. Ö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.

  2. Öffnen Sie die Datei mutint.py, die für Sie im Einrichtungsschritt erstellt wurde. In dieser Datei wird die Definition unserer MutInt - Klasse gespeichert.

  3. 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:

  1. Erstellen Sie eine neue Datei namens test_mutint.py im 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.

  1. 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.

✨ Lösung prüfen und üben

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.

  1. Öffnen Sie mutint.py in 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 die str() - 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 die eval() - Funktion übergeben wird, das Objekt neu erstellen würde.
  • __format__(): Diese Methode ermöglicht es Ihnen, Python's Zeichenkettenformatierungssystem mit Ihren MutInt - Objekten zu verwenden. Sie können Formatangaben wie Auffüllung und Zahlenformatierung nutzen.
  1. 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.

  1. 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.

✨ Lösung prüfen und üben

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.

  1. Öffnen Sie mutint.py in 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 unserem MutInt - Objekt auf der linken Seite verwendet wird. Innerhalb dieser Methode überprüfen wir zunächst, ob der other - Operand eine Instanz von MutInt oder ein int ist. Wenn dies der Fall ist, führen wir die Addition aus und geben ein neues MutInt - Objekt mit dem Ergebnis zurück. Wenn der other - Operand etwas anderes ist, geben wir NotImplemented zurück. Dies sagt Python, andere Methoden zu versuchen oder einen TypeError auszulösen.
  • __radd__(): Diese Methode wird aufgerufen, wenn der + - Operator mit unserem MutInt - Objekt auf der rechten Seite verwendet wird. Da die Addition eine kommutative Operation ist (d. h., a + b ist dasselbe wie b + a), können wir einfach die __add__ - Methode wiederverwenden.
  • __iadd__(): Diese Methode wird aufgerufen, wenn der += - Operator auf unserem MutInt - Objekt verwendet wird. Anstatt ein neues Objekt zu erstellen, modifiziert sie das vorhandene MutInt - Objekt und gibt es zurück.
  1. 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.

  1. 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
✨ Lösung prüfen und üben

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.

  1. Öffnen Sie mutint.py in 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:

  1. Importieren und verwenden Sie den @total_ordering - Dekorator aus dem functools - 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.

  2. Fügen Sie die __eq__() - Methode hinzu, um Gleichheitsvergleiche (==) zu behandeln. Diese Methode wird verwendet, um zu prüfen, ob zwei MutInt - Objekte oder ein MutInt - Objekt und eine Ganzzahl denselben Wert haben.

  3. Fügen Sie die __lt__() - Methode hinzu, um "kleiner - als" - Vergleiche (<) zu behandeln. Diese Methode wird verwendet, um festzustellen, ob ein MutInt - Objekt oder ein MutInt - Objekt im Vergleich zu einer Ganzzahl einen kleineren Wert hat.

  4. 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.

  1. 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.

✨ Lösung prüfen und üben

Hinzufügen von Typkonvertierungen

Unsere MutInt - Klasse unterstützt derzeit Addition und Vergleichsoperationen. Allerdings funktioniert sie nicht mit Python's eingebauten Konvertierungsfunktionen wie int() und float(). Diese Konvertierungsfunktionen sind in Python sehr nützlich. Beispielsweise wenn Sie einen Wert in eine Ganzzahl oder eine Fließkommazahl umwandeln möchten, um verschiedene Berechnungen oder Operationen durchzuführen, verlassen Sie sich auf diese Funktionen. Fügen wir daher unserer MutInt - Klasse die Fähigkeit hinzu, mit ihnen zu arbeiten.

  1. Öffnen Sie mutint.py in 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

    def __int__(self):
        """Convert to int."""
        return self.value

    def __float__(self):
        """Convert to float."""
        return float(self.value)

    __index__ = __int__  ## Support array indexing and other operations requiring an index

Wir haben drei neue Methoden zur MutInt - Klasse hinzugefügt:

  1. __int__(): Diese Methode wird aufgerufen, wenn Sie die int() - Funktion auf ein Objekt unserer MutInt - Klasse anwenden. Beispielsweise, wenn Sie ein MutInt - Objekt a haben und Sie int(a) schreiben, wird Python die __int__() - Methode des a - Objekts aufrufen.
  2. __float__(): Ebenso wird diese Methode aufgerufen, wenn Sie die float() - Funktion auf unser MutInt - Objekt anwenden.
  3. __index__(): Diese Methode wird für Operationen verwendet, die speziell einen Ganzzahlindex erfordern. Beispielsweise, wenn Sie ein Element in einer Liste über einen Index zugreifen möchten oder Bitlängenoperationen durchführen, benötigt Python einen Ganzzahlindex.

Die __index__ - Methode ist für Operationen, die einen Ganzzahlindex erfordern, wie Listenindizierung, Slicing und Bitlängenoperationen, von entscheidender Bedeutung. In unserer einfachen Implementierung setzen wir sie gleich __int__, da der Wert unseres MutInt - Objekts direkt als Ganzzahlindex verwendet werden kann.

  1. Erstellen Sie eine neue Testdatei namens test_conversions.py, um diese neuen Methoden zu testen:
## test_conversions.py

from mutint import MutInt

## Create a MutInt object
a = MutInt(3)

## Test conversions
print(f"int(a): {int(a)}")
print(f"float(a): {float(a)}")

## Test using as an index
names = ['Dave', 'Guido', 'Paula', 'Thomas', 'Lewis']
print(f"names[a]: {names[a]}")

## Test using in bit operations (requires __index__)
print(f"1 << a: {1 << a}")  ## Shift left by 3

## Test hex/oct/bin functions (requires __index__)
print(f"hex(a): {hex(a)}")
print(f"oct(a): {oct(a)}")
print(f"bin(a): {bin(a)}")

## Modify and test again
a.value = 5
print(f"\nAfter changing value to 5:")
print(f"int(a): {int(a)}")
print(f"names[a]: {names[a]}")
  1. Führen Sie das Testskript aus:
python3 /home/labex/project/test_conversions.py

Sie sollten eine Ausgabe ähnlich der folgenden sehen:

int(a): 3
float(a): 3.0
names[a]: Paula
1 << a: 8
hex(a): 0x3
oct(a): 0o3
bin(a): 0b11

After changing value to 5:
int(a): 5
names[a]: Lewis

Jetzt kann unsere MutInt - Klasse in Standard - Python - Typen umgewandelt werden und in Operationen verwendet werden, die einen Ganzzahlindex erfordern.

Die __index__ - Methode ist besonders wichtig. Sie wurde in Python eingeführt, um es Objekten zu ermöglichen, in Situationen verwendet zu werden, in denen ein Ganzzahlindex erforderlich ist, wie z. B. bei der Listenindizierung, bitweisen Operationen und verschiedenen Funktionen wie hex(), oct() und bin().

Mit diesen Ergänzungen ist unsere MutInt - Klasse jetzt eine ziemlich vollständige primitive Typklasse. Sie kann in den meisten Kontexten verwendet werden, in denen eine normale Ganzzahl verwendet würde, mit dem zusätzlichen Vorteil, dass sie veränderlich 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:
    """
    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

    def __int__(self):
        """Convert to int."""
        return self.value

    def __float__(self):
        """Convert to float."""
        return float(self.value)

    __index__ = __int__  ## Support array indexing and other operations requiring an index

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.

✨ Lösung prüfen und üben

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.