Ententypisierung, Datenvalidierung und durchsetzungsfähige Programmierung in Python


10

Über das Tippen von Enten :

Die Typisierung von Enten wird dadurch unterstützt, dass gewohnheitsmäßig nicht auf die Art der Argumente in Methoden- und Funktionskörpern geprüft wird. Dabei wird auf Dokumentation, klaren Code und Tests zurückgegriffen, um die korrekte Verwendung sicherzustellen.

Informationen zur Argumentvalidierung (EAFP: Es ist einfacher, um Vergebung zu bitten als um Erlaubnis). Ein angepasstes Beispiel von hier :

... es wird als pythonischer angesehen:

def my_method(self, key):
    try:
        value = self.a_dict[member]
    except TypeError:
        # do something else

Dies bedeutet, dass jeder andere Benutzer Ihres Codes kein echtes Wörterbuch oder keine echte Unterklasse verwenden muss. Er kann jedes Objekt verwenden, das die Zuordnungsschnittstelle implementiert.

Leider ist es in der Praxis nicht so einfach. Was ist, wenn das Mitglied im obigen Beispiel eine Ganzzahl sein könnte? Ganzzahlen sind unveränderlich - daher ist es durchaus sinnvoll, sie als Wörterbuchschlüssel zu verwenden. Sie werden jedoch auch zum Indizieren von Objekten vom Typ Sequenz verwendet. Wenn das Mitglied zufällig eine Ganzzahl ist, können in Beispiel 2 Listen und Zeichenfolgen sowie Wörterbücher durchgelassen werden.

Über durchsetzungsfähige Programmierung:

Behauptungen sind eine systematische Methode, um zu überprüfen, ob der interne Status eines Programms dem vom Programmierer erwarteten entspricht, mit dem Ziel, Fehler zu erkennen. Insbesondere sind sie gut geeignet, um falsche Annahmen zu erfassen, die beim Schreiben des Codes getroffen wurden, oder um eine Schnittstelle durch einen anderen Programmierer zu missbrauchen. Darüber hinaus können sie in gewissem Umfang als Inline-Dokumentation dienen, indem die Annahmen des Programmierers offensichtlich gemacht werden. ("Explizit ist besser als implizit.")

Die genannten Konzepte stehen manchmal in Konflikt, daher zähle ich bei der Auswahl auf die folgenden Faktoren, wenn ich überhaupt keine Datenvalidierung durchführe, eine starke Validierung durchführe oder Asserts verwende:

  1. Starke Validierung. Mit starker Validierung meine ich das Auslösen einer benutzerdefinierten Ausnahme ( ApiErrorzum Beispiel). Wenn meine Funktion / Methode Teil einer öffentlichen API ist, ist es besser, das Argument zu überprüfen, um eine gute Fehlermeldung über einen unerwarteten Typ anzuzeigen. Mit der Überprüfung des Typs meine ich nicht nur die Verwendung isinstance, sondern auch, ob das übergebene Objekt die erforderliche Schnittstelle unterstützt (Ententypisierung). Während ich die API dokumentiere und den erwarteten Typ spezifiziere und der Benutzer meine Funktion möglicherweise auf unerwartete Weise verwenden möchte, fühle ich mich sicherer, wenn ich die Annahmen überprüfe. Normalerweise verwende ich isinstanceund wenn ich später andere Typen oder Enten unterstützen möchte, ändere ich die Validierungslogik.

  2. Durchsetzungsfähige Programmierung. Wenn mein Code neu ist, verwende ich viel Asserts. Was raten Sie dazu? Entfernen Sie später Asserts aus dem Code?

  3. Wenn meine Funktion / Methode nicht Teil einer API ist, aber einige ihrer Argumente an einen anderen Code weiterleitet, der nicht von mir geschrieben, studiert oder getestet wurde, mache ich viele Asserts gemäß der aufgerufenen Schnittstelle. Meine Logik dahinter - scheitern Sie besser in meinem Code, dann irgendwo 10 Ebenen tiefer im Stacktrace mit unverständlichem Fehler, der dazu zwingt, viel zu debuggen und die Behauptung später trotzdem zu meinem Code hinzuzufügen.

Kommentare und Ratschläge zur Verwendung oder Nichtverwendung der Typ- / Wertvalidierung, behauptet? Entschuldigung für die nicht beste Formulierung der Frage.

Betrachten Sie beispielsweise die folgende Funktion, bei der Customeres sich um ein deklaratives SQLAlchemy-Modell handelt:

def add_customer(self, customer):
    """Save new customer into the database.
    @param customer: Customer instance, whose id is None
    @return: merged into global session customer
    """
    # no validation here at all
    # let's hope SQLAlchemy session will break if `customer` is not a model instance
    customer = self.session.add(customer)
    self.session.commit()
    return customer

Es gibt also mehrere Möglichkeiten, mit der Validierung umzugehen:

def add_customer(self, customer):
    # this is an API method, so let's validate the input
    if not isinstance(customer, Customer):
        raise ApiError('Invalid type')
    if customer.id is not None:
        raise ApiError('id should be None')

    customer = self.session.add(customer)
    self.session.commit()
    return customer

oder

def add_customer(self, customer):
    # this is an internal method, but i want to be sure
    # that it's a customer model instance
    assert isinstance(customer, Customer), 'Achtung!'
    assert customer.id is None

    customer = self.session.add(customer)
    self.session.commit()
    return customer

Wann und warum sollten Sie diese im Zusammenhang mit der Eingabe von Enten, der Typprüfung und der Datenvalidierung verwenden?


1
Sie sollten Asserts nicht wie Unit-Tests entfernen, es sei denn aus Leistungsgründen
Bryan Chen

Antworten:


4

Lassen Sie mich einige Leitprinzipien geben.

Prinzip Nr. 1. Wie unter http://docs.python.org/2/reference/simple_stmts.html beschrieben, kann der Leistungsaufwand für Asserts mit einer Befehlszeilenoption entfernt werden, während das Debuggen weiterhin möglich ist. Wenn die Leistung ein Problem darstellt, tun Sie dies. Lass die Behauptungen. (Aber tu nichts Wichtiges in den Behauptungen!)

Prinzip Nr. 2. Wenn Sie etwas behaupten und einen schwerwiegenden Fehler haben, verwenden Sie eine Bestätigung. Es ist absolut wertlos, etwas anderes zu tun. Wenn jemand das später ändern möchte, kann er Ihren Code ändern oder diesen Methodenaufruf vermeiden.

Prinzip Nr. 3. Verbieten Sie etwas nicht, nur weil Sie denken, dass es eine dumme Sache ist, dies zu tun. Was ist, wenn Ihre Methode Strings durchlässt? Wenn es funktioniert, funktioniert es.

Prinzip Nr. 4. Verbieten Sie Dinge, die Anzeichen für wahrscheinliche Fehler sind. Erwägen Sie beispielsweise, ein Wörterbuch mit Optionen zu erhalten. Wenn dieses Wörterbuch Dinge enthält, die keine gültigen Optionen sind, ist dies ein Zeichen dafür, dass jemand Ihre API nicht verstanden hat oder einen Tippfehler hatte. Wenn Sie das in die Luft jagen, ist es wahrscheinlicher, dass Sie einen Tippfehler bekommen, als dass Sie jemanden davon abhalten, etwas Vernünftiges zu tun.

Basierend auf den ersten beiden Prinzipien kann Ihre zweite Version weggeworfen werden. Welche der beiden anderen Sie bevorzugen, ist Geschmackssache. Was denkst du wahrscheinlicher? Dass jemand einen Nicht-Kunden weitergibt add_customerund die Dinge kaputt gehen (in diesem Fall wird Version 3 bevorzugt), oder dass jemand Ihren Kunden irgendwann durch ein Proxy-Objekt ersetzen möchte, das auf alle richtigen Methoden reagiert (In diesem Fall wird Version 1 bevorzugt).

Persönlich habe ich beide Fehlermodi gesehen. Ich würde eher mit Version 1 aus dem allgemeinen Prinzip heraus gehen, dass ich faul bin und weniger tippe. (Auch diese Art von Fehler tritt normalerweise früher oder später auf ziemlich offensichtliche Weise auf. Und wenn ich ein Proxy-Objekt verwenden möchte, ärgere ich mich sehr über Leute, die mir die Hände gebunden haben.) Aber es gibt Programmierer, die ich respektiere würde den anderen Weg gehen.


Ich bevorzuge v.3, besonders beim Entwerfen der Benutzeroberfläche - Schreiben neuer Klassen und Methoden. Ich halte v.3 auch für nützlich für API-Methoden - weil mein Code für andere neu ist. Ich halte einen durchsetzungsfähigen Ansatz für einen guten Kompromiss, da er in der Produktion entfernt wird, wenn er im optimierten Modus ausgeführt wird. > Wenn Sie dies in die Luft jagen, ist es wahrscheinlicher, dass Sie einen Tippfehler bemerken, als dass Sie jemanden davon abhalten, etwas Vernünftiges zu tun. <Es macht Ihnen also nichts aus, eine solche Validierung zu haben?
Warvariuc

Sagen wir mal so. Ich finde, dass die Vererbung schlecht dazu passt, wie ich Designs weiterentwickeln möchte. Ich bevorzuge Komposition. Ich scheue mich also zu behaupten, dass dies von dieser Klasse sein muss. Aber ich bin nicht gegen Behauptungen, bei denen ich denke, dass sie mir etwas retten.
Btilly
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.