Lernen Sie alles über Klassen-Dekorateure

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 lernen Sie über Klassen-Dekorateure (class decorators) in Python kennen und besprechen und erweitern Python-Deskriptoren (Python descriptors). Indem Sie diese Konzepte kombinieren, können Sie leistungsstarke und saubere Code-Strukturen erstellen.

In diesem Lab bauen Sie auf früheren Deskriptor-Konzepten auf und erweitern sie mithilfe von Klassen-Dekorateuren. Diese Kombination ermöglicht es Ihnen, saubereren und wartbareren Code mit verbesserten Validierungsfunktionen zu erstellen. Die zu ändernden Dateien sind validate.py und structure.py.

Implementierung von Typüberprüfung mit Deskriptoren

In diesem Schritt werden wir eine Stock-Klasse erstellen, die Deskriptoren (descriptors) zur Typüberprüfung verwendet. Aber zunächst verstehen wir, was Deskriptoren sind. Deskriptoren sind eine sehr leistungsstarke Funktion in Python. Sie ermöglichen es Ihnen, die Art und Weise zu kontrollieren, wie Attribute in Klassen zugegriffen werden.

Deskriptoren sind Objekte, die definieren, wie auf Attribute anderer Objekte zugegriffen wird. Sie tun dies, indem sie spezielle Methoden wie __get__, __set__ und __delete__ implementieren. Diese Methoden ermöglichen es den Deskriptoren, zu verwalten, wie Attribute abgerufen, festgelegt und gelöscht werden. Deskriptoren sind sehr nützlich für die Implementierung von Validierung, Typüberprüfung und berechneten Eigenschaften. Beispielsweise können Sie einen Deskriptor verwenden, um sicherzustellen, dass ein Attribut immer eine positive Zahl oder eine Zeichenkette eines bestimmten Formats ist.

Die Datei validate.py enthält bereits Validator-Klassen (String, PositiveInteger, PositiveFloat). Wir können diese Klassen verwenden, um die Attribute unserer Stock-Klasse zu validieren.

Jetzt erstellen wir unsere Stock-Klasse mit Deskriptoren.

  1. Öffnen Sie zunächst die Datei stock.py im Editor. Sie können dies tun, indem Sie den folgenden Befehl in Ihrem Terminal ausführen:
code ~/project/stock.py

Dieser Befehl verwendet den code-Editor, um die Datei stock.py im Verzeichnis ~/project zu öffnen.

  1. Sobald die Datei geöffnet ist, ersetzen Sie den Platzhalterinhalt durch den folgenden Code:
## stock.py

from structure import Structure
from validate import String, PositiveInteger, PositiveFloat

class Stock(Structure):
    _fields = ('name', 'shares', 'price')
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

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

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

## Create an __init__ method based on _fields
Stock.create_init()

Lassen Sie uns analysieren, was dieser Code tut. Das Tupel _fields definiert die Attribute der Stock-Klasse. Dies sind die Namen der Attribute, die unsere Stock-Objekte haben werden.

Die Attribute name, shares und price sind als Deskriptor-Objekte definiert. Der String()-Deskriptor stellt sicher, dass das name-Attribut eine Zeichenkette ist. Der PositiveInteger()-Deskriptor sorgt dafür, dass das shares-Attribut eine positive Ganzzahl ist. Und der PositiveFloat()-Deskriptor garantiert, dass das price-Attribut eine positive Fließkommazahl ist.

Die cost-Eigenschaft ist eine berechnete Eigenschaft. Sie berechnet die Gesamtkosten der Aktie basierend auf der Anzahl der Aktien und dem Preis pro Aktie.

Die sell-Methode wird verwendet, um die Anzahl der Aktien zu reduzieren. Wenn Sie diese Methode mit einer Anzahl von Aktien zum Verkauf aufrufen, subtrahiert sie diese Anzahl vom shares-Attribut.

Die Zeile Stock.create_init() erstellt dynamisch eine __init__-Methode für unsere Klasse. Diese Methode ermöglicht es uns, Stock-Objekte zu erstellen, indem wir die Werte für die Attribute name, shares und price übergeben.

  1. Nachdem Sie den Code hinzugefügt haben, speichern Sie die Datei. Dadurch werden Ihre Änderungen gespeichert und können verwendet werden, wenn Sie die Tests ausführen.

  2. Jetzt führen wir die Tests aus, um Ihre Implementierung zu überprüfen. Ändern Sie zunächst das Verzeichnis in das ~/project-Verzeichnis, indem Sie den folgenden Befehl ausführen:

cd ~/project

Führen Sie dann die Tests mit dem folgenden Befehl aus:

python3 teststock.py

Wenn Ihre Implementierung korrekt ist, sollten Sie eine Ausgabe ähnlich der folgenden sehen:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK

Diese Ausgabe bedeutet, dass alle Tests bestanden werden. Die Deskriptoren validieren erfolgreich die Typen jedes Attributs!

Versuchen wir, ein Stock-Objekt in der Python-Shell zu erstellen. Stellen Sie zunächst sicher, dass Sie sich im Verzeichnis ~/project befinden. Führen Sie dann den folgenden Befehl aus:

cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); print(s); print(f'Cost: {s.cost}')"

Sie sollten die folgende Ausgabe sehen:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Sie haben erfolgreich Deskriptoren für die Typüberprüfung implementiert! Jetzt verbessern wir diesen Code weiter.

✨ Lösung prüfen und üben

Erstellen eines Klassen-Dekorateurs zur Validierung

Im vorherigen Schritt hat unsere Implementierung funktioniert, aber es gab eine Redundanz. Wir mussten sowohl das _fields-Tupel als auch die Deskriptor-Attribute angeben. Dies ist nicht sehr effizient, und wir können es verbessern. In Python sind Klassen-Dekorateure (class decorators) ein leistungsstarkes Werkzeug, das uns helfen kann, diesen Prozess zu vereinfachen. Ein Klassen-Dekorator ist eine Funktion, die eine Klasse als Argument nimmt, sie auf irgendeine Weise modifiziert und dann die modifizierte Klasse zurückgibt. Indem wir einen Klassen-Dekorator verwenden, können wir automatisch Feldinformationen aus den Deskriptoren extrahieren, was unseren Code sauberer und wartbarer machen wird.

Lassen Sie uns einen Klassen-Dekorator erstellen, um unseren Code zu vereinfachen. Hier sind die Schritte, die Sie befolgen müssen:

  1. Öffnen Sie zunächst die Datei structure.py. Sie können den folgenden Befehl im Terminal verwenden:
code ~/project/structure.py

Dieser Befehl wird die Datei structure.py in Ihrem Code-Editor öffnen.

  1. Fügen Sie als Nächstes den folgenden Code ganz oben in die Datei structure.py ein, direkt nach allen Import-Anweisungen. Dieser Code definiert unseren Klassen-Dekorator:
from validate import Validator

def validate_attributes(cls):
    """
    Class decorator that extracts Validator instances
    and builds the _fields list automatically
    """
    validators = []
    for name, val in vars(cls).items():
        if isinstance(val, Validator):
            validators.append(val)

    ## Set _fields based on validator names
    cls._fields = [val.name for val in validators]

    ## Create initialization method
    cls.create_init()

    return cls

Lassen Sie uns analysieren, was dieser Dekorator tut:

  • Zunächst erstellt er eine leere Liste namens validators. Dann iteriert er über alle Attribute der Klasse mithilfe von vars(cls).items(). Wenn ein Attribut eine Instanz der Validator-Klasse ist, fügt er dieses Attribut der validators-Liste hinzu.
  • Danach setzt er das _fields-Attribut der Klasse. Er erstellt eine Liste von Namen aus den Validatoren in der validators-Liste und weist sie cls._fields zu.
  • Schließlich ruft er die create_init()-Methode der Klasse auf, um die __init__-Methode zu generieren, und gibt dann die modifizierte Klasse zurück.
  1. Sobald Sie den Code hinzugefügt haben, speichern Sie die Datei structure.py. Das Speichern der Datei stellt sicher, dass Ihre Änderungen beibehalten werden.

  2. Jetzt müssen wir die Datei stock.py ändern, um diesen neuen Dekorator zu verwenden. Öffnen Sie die Datei stock.py mit dem folgenden Befehl:

code ~/project/stock.py
  1. Aktualisieren Sie die Datei stock.py, um den validate_attributes-Dekorator zu verwenden. Ersetzen Sie den vorhandenen Code durch den folgenden:
## stock.py

from structure import Structure, validate_attributes
from validate import String, PositiveInteger, PositiveFloat

@validate_attributes
class Stock(Structure):
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

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

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

Beachten Sie die Änderungen, die wir vorgenommen haben:

  • Wir haben den @validate_attributes-Dekorator direkt über der Definition der Stock-Klasse hinzugefügt. Dies teilt Python mit, den validate_attributes-Dekorator auf die Stock-Klasse anzuwenden.
  • Wir haben die explizite _fields-Deklaration entfernt, da der Dekorator dies automatisch erledigt.
  • Wir haben auch den Aufruf von Stock.create_init() entfernt, da der Dekorator die Erstellung der __init__-Methode übernimmt.

Infolgedessen ist die Klasse jetzt einfacher und sauberer. Der Dekorator kümmert sich um alle Details, die wir früher manuell bearbeitet haben.

  1. Nach diesen Änderungen müssen wir überprüfen, ob alles noch wie erwartet funktioniert. Führen Sie die Tests erneut mit den folgenden Befehlen aus:
cd ~/project
python3 teststock.py

Wenn alles korrekt funktioniert, sollten Sie die folgende Ausgabe sehen:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK

Diese Ausgabe zeigt an, dass alle Tests erfolgreich bestanden wurden.

Lassen Sie uns auch unsere Stock-Klasse interaktiv testen. Führen Sie den folgenden Befehl im Terminal aus:

cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); print(s); print(f'Cost: {s.cost}')"

Sie sollten die folgende Ausgabe sehen:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Toll! Sie haben erfolgreich einen Klassen-Dekorator implementiert, der unseren Code vereinfacht, indem er Felddeklarationen und Initialisierung übernimmt. Dies macht unseren Code effizienter und leichter wartbar.

✨ Lösung prüfen und üben

Anwenden von Dekoratoren über Vererbung

In Schritt 2 haben wir einen Klassen-Dekorator (class decorator) erstellt, der unseren Code vereinfacht. Ein Klassen-Dekorator ist eine spezielle Art von Funktion, die eine Klasse als Argument nimmt und eine modifizierte Klasse zurückgibt. Es ist ein nützliches Werkzeug in Python, um Klassen Funktionalität hinzuzufügen, ohne ihren ursprünglichen Code zu ändern. Allerdings müssen wir immer noch den @validate_attributes-Dekorator explizit auf jede Klasse anwenden. Das bedeutet, dass jedes Mal, wenn wir eine neue Klasse erstellen, die Validierung benötigt, wir daran denken müssen, diesen Dekorator hinzuzufügen, was etwas umständlich sein kann.

Wir können dies weiter verbessern, indem wir den Dekorator automatisch über Vererbung anwenden. Vererbung ist ein grundlegendes Konzept in der objektorientierten Programmierung, bei dem eine Unterklasse (subclass) Attribute und Methoden von einer Elternklasse (parent class) erben kann. Python's __init_subclass__-Methode wurde in Python 3.6 eingeführt, um es Elternklassen zu ermöglichen, die Initialisierung von Unterklassen anzupassen. Das bedeutet, dass wenn eine Unterklasse erstellt wird, die Elternklasse einige Aktionen an ihr ausführen kann. Wir können diese Funktion nutzen, um unseren Dekorator automatisch auf jede Klasse anzuwenden, die von Structure erbt.

Lassen Sie uns dies implementieren:

  1. Öffnen Sie die Datei structure.py:
code ~/project/structure.py

Hier verwenden wir den code-Befehl, um die Datei structure.py in einem Code-Editor zu öffnen. Diese Datei enthält die Definition der Structure-Klasse, und wir werden sie ändern, um die __init_subclass__-Methode zu verwenden.

  1. Fügen Sie die __init_subclass__-Methode zur Structure-Klasse hinzu:
class Structure:
    _fields = ()
    _types = ()

    def __init__(self, *args):
        if len(args) != len(self._fields):
            raise TypeError(f'Expected {len(self._fields)} arguments')
        for name, val in zip(self._fields, args):
            setattr(self, name, val)

    def __repr__(self):
        values = ', '.join(repr(getattr(self, name)) for name in self._fields)
        return f'{type(self).__name__}({values})'

    @classmethod
    def create_init(cls):
        '''
        Create an __init__ method from _fields
        '''
        body = 'def __init__(self, %s):\n' % ', '.join(cls._fields)
        for name in cls._fields:
            body += f'    self.{name} = {name}\n'

        ## Execute the function creation code
        namespace = {}
        exec(body, namespace)
        setattr(cls, '__init__', namespace['__init__'])

    @classmethod
    def __init_subclass__(cls):
        validate_attributes(cls)

Die __init_subclass__-Methode ist eine Klassenmethode (class method), was bedeutet, dass sie auf die Klasse selbst und nicht auf eine Instanz der Klasse aufgerufen werden kann. Wenn eine Unterklasse von Structure erstellt wird, wird diese Methode automatisch aufgerufen. Innerhalb dieser Methode rufen wir den validate_attributes-Dekorator auf die Unterklasse cls auf. Auf diese Weise wird jedes Mal, wenn eine Unterklasse von Structure erstellt wird, automatisch das Validierungsverhalten hinzugefügt.

  1. Speichern Sie die Datei.

Nachdem wir die Datei structure.py geändert haben, müssen wir sie speichern, damit die Änderungen übernommen werden.

  1. Jetzt aktualisieren wir unsere stock.py-Datei, um diese neue Funktion zu nutzen:
code ~/project/stock.py

Wir öffnen die Datei stock.py, um sie zu ändern. Diese Datei enthält die Definition der Stock-Klasse, und wir werden sie so ändern, dass sie von der Structure-Klasse erbt, um die automatische Anwendung des Dekorators zu nutzen.

  1. Ändern Sie die stock.py-Datei, um den expliziten Dekorator zu entfernen:
## stock.py

from structure import Structure
from validate import String, PositiveInteger, PositiveFloat

class Stock(Structure):
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

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

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

Beachten Sie, dass wir:

  • Den Import von validate_attributes entfernt haben, da wir ihn nicht mehr explizit importieren müssen, da der Dekorator automatisch über Vererbung angewendet wird.
  • Den @validate_attributes-Dekorator entfernt haben, da die __init_subclass__-Methode in der Structure-Klasse die Anwendung übernimmt.
  • Der Code verlässt sich jetzt ausschließlich auf die Vererbung von Structure, um das Validierungsverhalten zu erhalten.
  1. Führen Sie die Tests erneut aus, um zu überprüfen, ob alles noch funktioniert:
cd ~/project
python3 teststock.py

Das Ausführen der Tests ist wichtig, um sicherzustellen, dass unsere Änderungen nichts kaputt gemacht haben. Wenn alle Tests bestanden werden, bedeutet das, dass die automatische Anwendung des Dekorators über Vererbung korrekt funktioniert.

Sie sollten alle Tests bestehen sehen:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK

Lassen Sie uns unsere Stock-Klasse erneut testen, um sicherzustellen, dass sie wie erwartet funktioniert:

cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); print(s); print(f'Cost: {s.cost}')"

Dieser Befehl erstellt eine Instanz der Stock-Klasse und gibt ihre Darstellung und die Kosten aus. Wenn die Ausgabe wie erwartet ist, bedeutet das, dass die Stock-Klasse mit der automatischen Anwendung des Dekorators korrekt funktioniert.

Ausgabe:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Diese Implementierung ist noch sauberer! Indem wir __init_subclass__ verwenden, haben wir die Notwendigkeit, Dekorateure explizit anzuwenden, beseitigt. Jede Klasse, die von Structure erbt, erhält automatisch das Validierungsverhalten.

✨ Lösung prüfen und üben

Hinzufügen von Funktionalität zur Zeilenkonvertierung

In der Programmierung ist es oft nützlich, Instanzen einer Klasse aus Datenzeilen zu erstellen, insbesondere wenn man mit Daten aus Quellen wie CSV-Dateien arbeitet. In diesem Abschnitt fügen wir die Möglichkeit hinzu, Instanzen der Structure-Klasse aus Datenzeilen zu erstellen. Wir werden dies tun, indem wir eine Klassenmethode from_row in der Structure-Klasse implementieren.

  1. Zunächst müssen Sie die Datei structure.py öffnen. Hier werden wir unsere Codeänderungen vornehmen. Verwenden Sie den folgenden Befehl in Ihrem Terminal:
code ~/project/structure.py
  1. Als Nächstes werden wir die Funktion validate_attributes ändern. Diese Funktion ist ein Klassen-Dekorator (class decorator), der Validator-Instanzen extrahiert und die Listen _fields und _types automatisch erstellt. Wir werden sie aktualisieren, um auch Typinformationen zu sammeln.
def validate_attributes(cls):
    """
    Class decorator that extracts Validator instances
    and builds the _fields and _types lists automatically
    """
    validators = []
    for name, val in vars(cls).items():
        if isinstance(val, Validator):
            validators.append(val)

    ## Set _fields based on validator names
    cls._fields = [val.name for val in validators]

    ## Set _types based on validator expected_types
    cls._types = [getattr(val, 'expected_type', lambda x: x) for val in validators]

    ## Create initialization method
    cls.create_init()

    return cls

In dieser aktualisierten Funktion sammeln wir das expected_type-Attribut von jedem Validator und speichern es in der Klassenvariablen _types. Dies wird später nützlich sein, wenn wir Daten aus Zeilen in die richtigen Typen umwandeln.

  1. Jetzt fügen wir die Klassenmethode from_row zur Structure-Klasse hinzu. Diese Methode ermöglicht es uns, eine Instanz der Klasse aus einer Datenzeile zu erstellen, die eine Liste oder ein Tupel sein kann.
@classmethod
def from_row(cls, row):
    """
    Create an instance from a data row (list or tuple)
    """
    rowdata = [func(val) for func, val in zip(cls._types, row)]
    return cls(*rowdata)

So funktioniert diese Methode:

  • Sie nimmt eine Datenzeile entgegen, die in Form einer Liste oder eines Tupels vorliegen kann.
  • Sie wandelt jeden Wert in der Zeile in den erwarteten Typ um, indem sie die entsprechende Funktion aus der _types-Liste verwendet.
  • Sie erstellt dann eine neue Instanz der Klasse mit den umgewandelten Werten und gibt sie zurück.
  1. Nach diesen Änderungen speichern Sie die Datei structure.py. Dadurch werden Ihre Codeänderungen beibehalten.

  2. Lassen Sie uns unsere from_row-Methode testen, um sicherzustellen, dass sie wie erwartet funktioniert. Wir werden einen einfachen Test mit der Stock-Klasse durchführen. Führen Sie den folgenden Befehl in Ihrem Terminal aus:

cd ~/project
python3 -c "from stock import Stock; s = Stock.from_row(['GOOG', '100', '490.1']); print(s); print(f'Cost: {s.cost}')"

Sie sollten eine Ausgabe ähnlich der folgenden sehen:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Beachten Sie, dass die Zeichenkettenwerte '100' und '490.1' automatisch in die richtigen Typen (Integer und Float) umgewandelt wurden. Dies zeigt, dass unsere from_row-Methode korrekt funktioniert.

  1. Schließlich versuchen wir, Daten aus einer CSV-Datei mit unserem reader.py-Modul zu lesen. Führen Sie den folgenden Befehl in Ihrem Terminal aus:
cd ~/project
python3 -c "from stock import Stock; import reader; portfolio = reader.read_csv_as_instances('portfolio.csv', Stock); print(portfolio); print(f'Total value: {sum(s.cost for s in portfolio)}')"

Sie sollten eine Ausgabe sehen, die die Aktien aus der CSV-Datei anzeigt:

[Stock('GOOG', 100, 490.1), Stock('AAPL', 50, 545.75), Stock('MSFT', 200, 30.47)]
Total value: 73444.0

Die from_row-Methode ermöglicht es uns, CSV-Daten einfach in Instanzen der Stock-Klasse umzuwandeln. In Kombination mit der Funktion read_csv_as_instances haben wir eine leistungsstarke Möglichkeit, strukturierte Daten zu laden und zu verarbeiten.

✨ Lösung prüfen und üben

Hinzufügen von Methoden-Argument-Validierung

In Python ist die Validierung von Daten ein wichtiger Bestandteil beim Schreiben robuster Code. In diesem Abschnitt gehen wir einen Schritt weiter und validieren automatisch die Argumente von Methoden. Die Datei validate.py enthält bereits einen @validated-Dekorator. Ein Dekorator in Python ist eine spezielle Funktion, die eine andere Funktion modifizieren kann. Der @validated-Dekorator hier kann die Argumente einer Funktion anhand ihrer Anmerkungen (annotations) überprüfen. Anmerkungen in Python sind eine Möglichkeit, Metadaten zu Funktionsparametern und Rückgabewerten hinzuzufügen.

Lassen Sie uns unseren Code ändern, um diesen Dekorator auf Methoden mit Anmerkungen anzuwenden:

  1. Zunächst müssen wir verstehen, wie der validated-Dekorator funktioniert. Öffnen Sie die Datei validate.py, um sie zu überprüfen:
code ~/project/validate.py

Der validated-Dekorator nutzt Funktionsanmerkungen, um Argumente zu validieren. Bevor die Funktion ausgeführt wird, überprüft er jedes Argument anhand seines Anmerkungstyps. Beispielsweise, wenn ein Argument als Ganzzahl (integer) annotiert ist, stellt der Dekorator sicher, dass der übergebene Wert tatsächlich eine Ganzzahl ist.

  1. Jetzt werden wir die Funktion validate_attributes in structure.py ändern, um annotierte Methoden mit dem validated-Dekorator zu umhüllen. Das bedeutet, dass alle Methoden mit Anmerkungen in der Klasse ihre Argumente automatisch validieren lassen. Öffnen Sie die Datei structure.py:
code ~/project/structure.py
  1. Aktualisieren Sie die Funktion validate_attributes:
def validate_attributes(cls):
    """
    Class decorator that:
    1. Extracts Validator instances and builds _fields and _types lists
    2. Applies @validated decorator to methods with annotations
    """
    ## Import the validated decorator
    from validate import validated

    ## Process validator descriptors
    validators = []
    for name, val in vars(cls).items():
        if isinstance(val, Validator):
            validators.append(val)

    ## Set _fields based on validator names
    cls._fields = [val.name for val in validators]

    ## Set _types based on validator expected_types
    cls._types = [getattr(val, 'expected_type', lambda x: x) for val in validators]

    ## Apply @validated decorator to methods with annotations
    for name, val in vars(cls).items():
        if callable(val) and hasattr(val, '__annotations__'):
            setattr(cls, name, validated(val))

    ## Create initialization method
    cls.create_init()

    return cls

Diese aktualisierte Funktion macht jetzt Folgendes:

  1. Sie verarbeitet Validator-Deskriptoren wie zuvor. Validator-Deskriptoren werden verwendet, um Validierungsregeln für Klassenattribute zu definieren.

  2. Sie findet alle Methoden mit Anmerkungen in der Klasse. Anmerkungen werden an Methodenparameter hinzugefügt, um den erwarteten Typ des Arguments anzugeben.

  3. Sie wendet den @validated-Dekorator auf diese Methoden an. Dies stellt sicher, dass die an diese Methoden übergebenen Argumente gemäß ihren Anmerkungen validiert werden.

  4. Speichern Sie die Datei nach diesen Änderungen. Das Speichern der Datei ist wichtig, da es sicherstellt, dass unsere Änderungen gespeichert werden und später verwendet werden können.

  5. Jetzt aktualisieren wir die sell-Methode in der Stock-Klasse, um eine Anmerkung hinzuzufügen. Anmerkungen helfen dabei, den erwarteten Typ des Arguments anzugeben, der vom @validated-Dekorator für die Validierung verwendet wird. Öffnen Sie die Datei stock.py:

code ~/project/stock.py
  1. Ändern Sie die sell-Methode, um eine Typanmerkung hinzuzufügen:
## stock.py

from structure import Structure
from validate import String, PositiveInteger, PositiveFloat

class Stock(Structure):
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

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

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

Die wichtigste Änderung ist das Hinzufügen von : PositiveInteger zum Parameter nshares. Dies sagt Python (und unserem @validated-Dekorator) aus, dass dieses Argument mit dem PositiveInteger-Validator validiert werden soll. Wenn wir also die sell-Methode aufrufen, muss das nshares-Argument eine positive Ganzzahl sein.

  1. Führen Sie die Tests erneut aus, um sicherzustellen, dass alles noch funktioniert. Das Ausführen von Tests ist eine gute Möglichkeit, um sicherzustellen, dass unsere Änderungen keine bestehende Funktionalität zerstört haben.
cd ~/project
python3 teststock.py

Sie sollten alle Tests bestehen sehen:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK
  1. Lassen Sie uns unsere neue Argumentvalidierung testen. Wir versuchen, die sell-Methode mit gültigen und ungültigen Argumenten aufzurufen, um zu sehen, ob die Validierung wie erwartet funktioniert.
cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); s.sell(25); print(s); try: s.sell(-25); except Exception as e: print(f'Error: {e}')"

Sie sollten eine Ausgabe ähnlich der folgenden sehen:

Stock('GOOG', 75, 490.1)
Error: Bad Arguments
  nshares: must be >= 0

Dies zeigt, dass unsere Methoden-Argument-Validierung funktioniert! Der erste Aufruf von sell(25) ist erfolgreich, da 25 eine positive Ganzzahl ist. Aber der zweite Aufruf von sell(-25) schlägt fehl, da -25 keine positive Ganzzahl ist.

Sie haben jetzt ein komplettes System implementiert für:

  1. Die Validierung von Klassenattributen mit Deskriptoren. Deskriptoren werden verwendet, um Validierungsregeln für Klassenattribute zu definieren.
  2. Das automatische Sammeln von Feldinformationen mit Klassen-Dekoratoren. Klassen-Dekorateure können das Verhalten einer Klasse ändern, wie z.B. das Sammeln von Feldinformationen.
  3. Das Konvertieren von Zeilendaten in Instanzen. Dies ist nützlich, wenn man mit Daten aus externen Quellen arbeitet.
  4. Die Validierung von Methodenargumenten mit Anmerkungen. Anmerkungen helfen dabei, den erwarteten Typ des Arguments für die Validierung anzugeben.

Dies zeigt die Stärke der Kombination von Deskriptoren und Dekoratoren in Python, um ausdrucksstarke, selbstvalidierende Klassen zu erstellen.

✨ Lösung prüfen und üben

Zusammenfassung

In diesem Lab haben Sie gelernt, wie Sie leistungsstarke Python-Funktionen kombinieren können, um sauberen, selbstvalidierenden Code zu erstellen. Sie haben wichtige Konzepte beherrscht, wie die Verwendung von Deskriptoren (descriptors) zur Attributvalidierung, die Erstellung von Klassen-Dekoratoren (class decorators) zur Automatisierung der Codegenerierung und die automatische Anwendung von Dekoratoren durch Vererbung.

Diese Techniken sind leistungsstarke Werkzeuge für die Erstellung von robustem und wartbarem Python-Code. Sie ermöglichen es Ihnen, Validierungsanforderungen klar auszudrücken und sie in Ihrem gesamten Codebase durchzusetzen. Sie können diese Muster jetzt in Ihren eigenen Python-Projekten anwenden, um die Codequalität zu verbessern und Boilerplate-Code zu reduzieren.