Wie prüfe ich, ob ein Objekt von einem bestimmten Typ ist oder ob es von einem bestimmten Typ erbt?
Wie kann ich überprüfen, ob das Objekt o
ist vom Typ str
?
Wie prüfe ich, ob ein Objekt von einem bestimmten Typ ist oder ob es von einem bestimmten Typ erbt?
Wie kann ich überprüfen, ob das Objekt o
ist vom Typ str
?
Die akzeptierte Antwort beantwortet die Frage, indem sie die Antworten auf die gestellten Fragen liefert.
F: Wie kann man am besten prüfen, ob ein bestimmtes Objekt von einem bestimmten Typ ist? Wie wäre es zu prüfen, ob das Objekt von einem bestimmten Typ erbt?
A: Verwendung
isinstance, issubclass, type
auf der Grundlage von Typen zu prüfen.
Wie in anderen Antworten und Kommentaren jedoch schnell deutlich wird, steckt hinter der Idee der "Typüberprüfung" in Python noch viel mehr. Seit der Einführung von Python 3 und Typ-Hinweise hat sich ebenfalls viel verändert. Im Folgenden gehe ich auf einige der Schwierigkeiten bei der Typprüfung, der Duck-Typisierung und der Ausnahmebehandlung ein. Für diejenigen, die der Meinung sind, dass eine Typüberprüfung nicht notwendig ist (normalerweise ist sie es nicht, aber wir sind ja hier), zeige ich auch auf, wie stattdessen Typ-Hinweise verwendet werden können.
Eine Typüberprüfung ist in Python nicht immer sinnvoll. Betrachten Sie das folgende Beispiel:
def sum(nums):
"""Expect an iterable of integers and return the sum."""
result = 0
for n in nums:
result += n
return result
Um zu prüfen, ob die Eingabe eine Iterable von ganzen Zahlen ist, stoßen wir auf ein großes Problem. Die einzige Möglichkeit zu prüfen, ob jedes Element eine ganze Zahl ist, wäre eine Schleife, um jedes Element zu prüfen. Wenn wir jedoch den gesamten Iterator in einer Schleife durchlaufen, bleibt nichts für den vorgesehenen Code übrig. In einer solchen Situation haben wir zwei Möglichkeiten.
In der Schleife prüfen.
Prüfen Sie vorher, aber bewahren Sie alles so auf, wie wir es prüfen.
Option 1 hat den Nachteil, dass sie unseren Code verkompliziert, insbesondere wenn wir an vielen Stellen ähnliche Prüfungen durchführen müssen. Sie zwingt uns, die Typüberprüfung vom Anfang der Funktion nach überall verwenden wir die Iterable in unserem Code.
Option 2 hat den offensichtlichen Nachteil, dass sie den gesamten Zweck von Iteratoren zunichte macht. Der ganze Sinn besteht darin, die Daten nicht zu speichern, weil wir das nicht brauchen sollten.
Man könnte auch denken, dass die Überprüfung, ob die Überprüfung aller Elemente ist zu viel, dann vielleicht können wir nur prüfen, ob die Eingabe selbst ist vom Typ iterable, aber es gibt nicht wirklich eine iterable Basisklasse. Jeder Typ, der __iter__
ist iterierbar.
Ein alternativer Ansatz wäre, ganz auf die Typüberprüfung zu verzichten und sich stattdessen auf die Behandlung von Ausnahmen und die Typisierung von Enten zu konzentrieren. Das heißt, Sie verpacken Ihren Code in einen try-except-Block und fangen alle Fehler ab, die auftreten. Alternativ können Sie auch gar nichts tun und Ausnahmen auf natürliche Weise aus Ihrem Code entstehen lassen.
Hier ist eine Möglichkeit, eine Ausnahme abzufangen.
def sum(nums):
"""Try to catch exceptions?"""
try:
result = 0
for n in nums:
result += n
return result
except TypeError as e:
print(e)
Im Vergleich zu den bisherigen Möglichkeiten ist dies sicherlich besser. Wir prüfen, während wir den Code ausführen. Wenn es eine TypeError
irgendwo, werden wir es wissen. Wir müssen nicht überall, wo wir die Eingabe in einer Schleife durchlaufen, eine Prüfung durchführen. Und wir müssen die Eingabe nicht speichern, wenn wir sie durchlaufen.
Außerdem ermöglicht dieser Ansatz die Typisierung von Enten. Anstatt zu prüfen, ob specific types
sind wir dazu übergegangen, zu prüfen, ob specific behaviors
und suchen, wenn sich die Eingabe nicht wie erwartet verhält (in diesem Fall durchlaufen wir eine Schleife nums
und die Möglichkeit, die n
).
Doch genau die Gründe, die die Ausnahmebehandlung so angenehm machen, können auch ihr Verhängnis sein.
A float
ist kein int
aber sie erfüllt die Verhalten Anforderungen an die Arbeit.
Es ist auch eine schlechte Praxis, den gesamten Code mit einem try-except-Block zu umhüllen.
Auf den ersten Blick mag dies nicht als Problem erscheinen, aber hier sind einige Gründe, die Ihre Meinung ändern könnten.
Ein Benutzer kann nicht mehr erwarten, dass unsere Funktion eine int
wie vorgesehen. Dies kann den Code an anderer Stelle brechen.
Da Ausnahmen aus einer Vielzahl von Quellen stammen können, kann die Verwendung von try-except für den gesamten Codeblock dazu führen, dass Ausnahmen abgefangen werden, die Sie nicht beabsichtigt haben. Wir wollten nur prüfen, ob nums
war iterierbar und hatte ganzzahlige Elemente.
Im Idealfall möchten wir die Ausnahmen, die unsere Codegeneratoren erzeugen, abfangen und stattdessen informativere Ausnahmen auslösen. Es ist nicht lustig, wenn eine Ausnahme aus dem Code eines anderen ausgelöst wird, ohne eine andere Erklärung als eine Zeile, die man nicht geschrieben hat, und dass einige TypeError
aufgetreten.
Um die Ausnahmebehandlung als Reaktion auf die obigen Punkte zu beheben, würde unser Code dann zu dieser... Abscheulichkeit werden.
def sum(nums):
"""
Try to catch all of our exceptions only.
Re-raise them with more specific details.
"""
result = 0
try:
iter(nums)
except TypeError as e:
raise TypeError("nums must be iterable")
for n in nums:
try:
result += int(n)
except TypeError as e:
raise TypeError("stopped mid iteration since a non-integer was found")
return result
Sie können sich denken, worauf das hinausläuft. Je mehr wir versuchen, Dinge "richtig" zu überprüfen, desto schlechter sieht unser Code aus. Verglichen mit dem ursprünglichen Code ist dieser überhaupt nicht lesbar.
Man könnte argumentieren, dass dies vielleicht ein bisschen extrem ist. Aber andererseits ist dies nur ein sehr einfaches Beispiel. In der Praxis ist Ihr Code wahrscheinlich viel komplizierter als dies.
Wir haben gesehen, was passiert, wenn wir versuchen, unser kleines Beispiel so zu ändern, dass die Typenprüfung aktiviert wird. Anstatt sich auf den Versuch zu konzentrieren, bestimmte Typen zu erzwingen, bietet das Type Hinting eine Möglichkeit, den Benutzern die Typen klar zu machen.
from typing import Iterable
def sum(nums: Iterable[int]) -> int:
result = 0
for n in nums:
result += n
return result
Hier sind einige Vorteile der Verwendung von Typ-Hinweisen.
Der Code sieht jetzt wirklich gut aus!
Die statische Typanalyse kann von Ihrem Editor durchgeführt werden, wenn Sie Typ-Hinweise verwenden!
Sie werden in der Funktion/Klasse gespeichert, so dass sie dynamisch verwendet werden können, z. B. typeguard
y dataclasses
.
Sie werden für Funktionen angezeigt, wenn Sie help(...)
.
Sie brauchen nicht zu prüfen, ob Ihr Eingabetyp auf der Grundlage einer Beschreibung oder, schlimmer noch, deren Fehlen richtig ist.
Sie können einen Hinweis basierend auf Struktur z. B. "Hat es dieses Attribut?", ohne dass der Benutzer eine Unterklassifizierung vornehmen muss.
Der Nachteil der Tipphilfe?
Mit anderen Worten, es beantwortet die Frage nicht wirklich, weil es keine Typüberprüfung bietet. Wenn Sie jedoch wegen der Typenprüfung hier sind, dann sollten Sie sollte auch ein Typ-Hinweis sein. Natürlich, wenn Sie zu dem Schluss gekommen sind, dass Typprüfung nicht wirklich notwendig ist, aber Sie wollen einen gewissen Anschein von Typisierung, dann Typ Hinweise sind für Sie.
Für Hugo:
Sie meinen wahrscheinlich list
statt array
Sie wollen nicht wissen, ob das betreffende Objekt eine Liste ist, sondern ob es sich um eine Art Sequenz oder ein einzelnes Objekt handelt. Versuchen Sie also, es wie eine Sequenz zu verwenden.
Sagen wir, Sie wollen das Objekt zu einer bestehenden Sequenz hinzufügen, oder, wenn es sich um eine Sequenz von Objekten handelt, sie alle hinzufügen
try:
my_sequence.extend(o)
except TypeError:
my_sequence.append(o)
Ein Trick dabei ist, wenn Sie mit Zeichenketten und/oder Zeichenkettenfolgen arbeiten - das ist knifflig, denn eine Zeichenkette wird oft als einzelnes Objekt betrachtet, ist aber auch eine Folge von Zeichen. Noch schlimmer ist, dass es sich in Wirklichkeit um eine Folge von Zeichenketten mit einfacher Länge handelt.
Ich entscheide mich in der Regel dafür, meine API so zu gestalten, dass sie nur entweder einen einzelnen Wert oder eine Sequenz akzeptiert - das macht die Sache einfacher. Es ist nicht schwer, eine [ ]
um Ihren Einzelwert herum, wenn Sie ihn bei Bedarf übergeben.
(Allerdings kann dies bei Zeichenketten zu Fehlern führen, da sie wie Sequenzen aussehen (sind).)
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
16 Stimmen
Ich glaube, Herr Coombs übersieht Beispiele wie nicht-JSON serialisierbare Klassen. Wenn man einen großen Datenblock durch eine Funktion schickt (deren Code man nicht beeinflussen kann), möchte man vielleicht bestimmte Teile dieser Daten in ein <str> konvertieren, bevor man sie weitergibt. Zumindest ist das so I landete auf dieser Seite...
7 Stimmen
Es scheint, dass der häufigste Grund für diese Frage darin besteht, dass man zwischen Zeichenketten und Iterablen von Zeichenketten unterscheiden möchte. Dies ist eine knifflige Frage, weil Strings sind Iterabilen von Zeichenketten - eine Ein-Zeichen-Zeichenkette ist sogar eine Folge von sich selbst (als ich das letzte Mal nachgesehen habe - man sollte sich wahrscheinlich nicht darauf verlassen). Aber würde jemand jemals Verwendung für etwas String-ähnliches haben? Oui . Die Antwort auf die Frage "Wie unterscheide ich zwischen Zeichenketten und anderen Iterabilen von Zeichenketten?" ist also richtig: "Das hängt davon ab, was Sie tun wollen" :-D
7 Stimmen
Python-Typ-Annotationen sind jetzt eine Sache. Werfen Sie einen Blick auf mypy