Was ist die kanonische Methode, um nach Python zu suchen?


1277

Wie kann am besten überprüft werden, ob ein bestimmtes Objekt von einem bestimmten Typ ist? Wie wäre es zu überprüfen, ob das Objekt von einem bestimmten Typ erbt?

Nehmen wir an, ich habe ein Objekt o. Wie überprüfe ich, ob es ein ist str?


7
Nun, der kanonische Ansatz in Python besteht darin, den Typ überhaupt nicht zu überprüfen (es sei denn, Sie debuggen). Normalerweise versuchen Sie nur, es als Zeichenfolge zu verwenden (z. B. mit anderen Zeichenfolgen verketten, auf Konsole drucken usw.). Wenn Sie glauben, dass dies fehlschlagen könnte, verwenden Sie try / exception oder hasattr. Die akzeptierte Antwort ist jedoch die kanonische Methode, um das zu tun, was Sie in der Python-Welt im Allgemeinen "nicht tun" sollten. Für weitere Informationen googeln Sie "Python duck typing" oder lesen Sie diese: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/…
Jon Coombs

9
Ich denke, Mr. Coombs übersieht Beispiele wie nicht JSON-serialisierbare Klassen. Wenn Sie einen großen Datenblock durch eine Funktion führen (deren Code man nicht beeinflussen kann), möchten Sie möglicherweise bestimmte Teile dieser Daten beispielsweise in ein <str> konvertieren, bevor Sie sie übergeben. Zumindest bin ich so auf dieser Seite gelandet ...
John Carrell

2
Es scheint der häufigste Grund dafür zu sein, dass man zwischen Strings und Iterables von Strings unterscheiden möchte. Dies ist eine schwierige Frage , weil Strings sind Iterables von Strings - ein Single-Zeichenkette ist auch eine Folge von sich selbst (letzte Mal habe ich geprüft - ein wahrscheinlich sollte nicht auf sie verlassen). Aber hätte jemand jemals etwas für etwas Saitenartiges gebraucht? Ja . Die Antwort auf "Was soll ich tun, um zwischen Zeichenfolgen und anderen iterierbaren Zeichenfolgen zu unterscheiden?" ist richtig: "Es hängt davon ab, was Sie versuchen zu tun". :-D
Clacke

2
Anmerkungen vom Typ Python sind jetzt eine Sache. Werfen
Sheena

Antworten:


1522

Verwenden Sie isinstance , um zu überprüfen, ob oes sich um eine Instanz stroder eine Unterklasse von handelt (dies wäre der "kanonische" Weg):str

if isinstance(o, str):

So überprüfen Sie, ob der Typ ogenau ist str(Unterklassen ausschließen):

if type(o) is str:

Folgendes funktioniert ebenfalls und kann in einigen Fällen nützlich sein:

if issubclass(type(o), str):

Relevante Informationen finden Sie unter Integrierte Funktionen in der Python-Bibliotheksreferenz.

Noch ein Hinweis: Wenn Sie in diesem Fall Python 2 verwenden, möchten Sie möglicherweise tatsächlich Folgendes verwenden:

if isinstance(o, basestring):

weil dies auch Unicode-Strings abfängt ( unicodeist keine Unterklasse von str; beide strund unicodesind Unterklassen von basestring). Beachten Sie, dass basestringes in Python 3 nicht mehr existiert, wo Strings ( ) und Binärdaten ( ) strikt getrennt werden .strbytes

Akzeptiert alternativ isinstanceein Tupel von Klassen. Dies wird zurückgegeben, Truewenn oes sich um eine Instanz einer Unterklasse handelt von (str, unicode):

if isinstance(o, (str, unicode)):

31
str .__ Unterklassen __ () geben nur die direkten Unterklassen von str zurück und tun nicht dasselbe wie issubclass () oder isinstance (). (Um dies zu tun, müssten Sie rekursiv .__ Unterklassen __ () aufrufen.
Thomas Wouters

15
Dies ist eine gute Antwort, aber ich denke, es sollte wirklich mit einer Warnung beginnen, dass Sie dies normalerweise nicht in Python tun sollten. So wie es ist, scheint es die Annahme zu bestätigen, dass dies eine "kanonische Sache in Python" ist, was es nicht ist.
Jon Coombs

4
Dies sind python2-Antworten. Zum Beispiel gibt es in Python3 keinen Basisring.
dfrankow

4
Was ist der Unterschied zwischen Instanz und "genau"? Wenn type(a) is Objectdann ist es nicht auch wahr, dass isinstance(a, Object). Wenn aber type(a) is SubClassOfObjectdann type(a) is Object == Falsedoch isinstance(a, Object) == True. Recht?
Mavavilj

1
@mavavilj - a is bbedeutet, dass a und b genau dasselbe sind, dh Verweise auf dieselbe Entität im Speicher. Also aund bmüsste genau die gleiche Klasse sein, keine Unterklassen wie bei isinstance(). Siehe zum Beispiel stackoverflow.com/a/133024/1072212
Terry Brown

196

Die meisten Pythonic Weg , um den Typ eines Objekts zu überprüfen , ist ... nicht zu überprüfen.

Da Python Duck Typing unterstützt , sollten Sie try...exceptdie Methoden des Objekts nur so verwenden, wie Sie sie verwenden möchten. Wenn Ihre Funktion also nach einem beschreibbaren Dateiobjekt sucht, überprüfen Sie nicht , ob es sich um eine Unterklasse filehandelt. Versuchen Sie einfach, die .write()Methode zu verwenden!

Natürlich brechen diese schönen Abstraktionen manchmal zusammen und isinstance(obj, cls)sind genau das, was Sie brauchen. Aber sparsam verwenden.


75
IMHO ist der pythonischste Weg, mit jedem Argument umzugehen, das gegeben wird. In meinem Code kann ich oft nicht wissen, ob ich ein Objekt oder ein Array von Objekten empfange, und ich verwende die Typprüfung intern, um ein einzelnes Objekt in eine Liste mit einem Element zu konvertieren.
Sastanin

14
Anstatt nur zu versuchen, die Schreibmethode zu verwenden, gibt es Zeiten, in denen Sie dies tun möchten, ohne eine Ausnahme zu verursachen. In diesem Fall könnten Sie tun ... if hasattr(ob, "write") and callable(ob.write): oder etwas func = getattr(ob, "write", None) if callable(func): ...
Diktatzugriff

142
Beim Entenschreiben geht es um die Verwendung einer Bibliothek. Bei der Typprüfung geht es darum , eine Bibliothek zu schreiben . Nicht die gleiche Problemdomäne.
RickyA

16
@ RickyA, ich bin anderer Meinung. Bei der Ententypisierung geht es um die Interaktion mit Objekten über Schnittstellen mit bekannter Semantik. Dies kann entweder für den Bibliothekscode oder für den Code gelten, der eine solche Bibliothek verwendet.
Dan Lenski

6
@ Nyuszika7h, In Python3 wird hasattrnur ein AttributeError unterdrückt - Siehe: docs.python.org/3.4/library/functions.html#hasattr
ideasman42

57

isinstance(o, str)wird zurückgegeben, Truewenn oes sich um einen stroder einen Typ handelt, von dem erbt str.

type(o) is strwird Truegenau dann zurückkehren, wenn oes sich um eine str handelt. Es wird zurückgegeben, Falsewenn oes sich um einen Typ handelt, der von erbt str.


6
Dies schlägt natürlich fehl, wenn das Objekt keine Instanz von 'str' ist, sondern von etwas String-ähnlichem. Wie Unicode, mmap, UserString oder ein anderer benutzerdefinierter Typ. Der übliche Ansatz in Python besteht darin, keine Typchecks durchzuführen.
Thomas Wouters

6
Sie müssen sich nicht entschuldigen, es ist in Ordnung, Ihre eigene Frage zu beantworten. SO ist für die Antworten, nicht das Karma.
Eli Bendersky

2
Das ist sehr hilfreich. Weil der Unterschied zwischen isinstanceund type(var) == type('')nicht klar ist.
Sastanin

30

Nachdem die Frage gestellt und beantwortet wurde, wurden Python Tipphinweise hinzugefügt . Typhinweise in Python ermöglichen die Überprüfung von Typen, jedoch auf eine ganz andere Weise als bei statisch typisierten Sprachen. Typhinweise in Python verknüpfen die erwarteten Argumenttypen mit Funktionen als zur Laufzeit zugängliche Daten, die Funktionen zugeordnet sind, und ermöglichen die Überprüfung von Typen. Beispiel für die Syntax von Typhinweisen:

def foo(i: int):
    return i

foo(5)
foo('oops')

In diesem Fall soll ein Fehler ausgelöst werden, foo('oops')da der mit Anmerkungen versehene Typ des Arguments lautet int. Der Mehr Typ Hinweis nicht dazu führen , ein Fehler auftreten , wenn das Skript normal ausgeführt wird. Der Funktion werden jedoch Attribute hinzugefügt, die die erwarteten Typen beschreiben, die andere Programme abfragen und zur Überprüfung auf Typfehler verwenden können.

Eines dieser anderen Programme, mit denen der Typfehler gefunden werden kann, ist mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Möglicherweise müssen Sie mypyvon Ihrem Paketmanager installieren . Ich glaube nicht, dass es mit CPython geliefert wird, aber es scheint ein gewisses Maß an "Offiziellität" zu haben.)

Die Typprüfung auf diese Weise unterscheidet sich von der Typprüfung in statisch typisierten kompilierten Sprachen. Da Typen in Python dynamisch sind, muss die Typprüfung zur Laufzeit durchgeführt werden, was selbst bei korrekten Programmen Kosten verursacht, wenn wir darauf bestehen, dass dies bei jeder Gelegenheit geschieht. Explizite Typprüfungen können auch restriktiver sein als erforderlich und unnötige Fehler verursachen (z. B. muss das Argument wirklich vom genauen listTyp sein oder ist etwas iterierbares ausreichend?).

Der Vorteil der expliziten Typprüfung besteht darin, dass Fehler früher erkannt und klarere Fehlermeldungen ausgegeben werden können als beim Entenschreiben. Die genauen Anforderungen eines Ententyps können nur mit externer Dokumentation ausgedrückt werden (hoffentlich gründlich und genau), und Fehler von inkompatiblen Typen können weit entfernt von ihrem Ursprung auftreten.

Die Typhinweise von Python sollen einen Kompromiss bieten, bei dem Typen angegeben und überprüft werden können, während der normalen Codeausführung jedoch keine zusätzlichen Kosten entstehen.

Das typingPaket bietet Typvariablen, die in Typhinweisen verwendet werden können, um das erforderliche Verhalten auszudrücken, ohne dass bestimmte Typen erforderlich sind. Beispielsweise enthält es Variablen wie Iterableund Callablefür Hinweise, um die Notwendigkeit eines beliebigen Typs mit diesen Verhaltensweisen anzugeben.

Während Typhinweise die pythonischste Methode sind, um Typen zu überprüfen, ist es oft noch pythonischer, Typen überhaupt nicht zu überprüfen und sich auf die Eingabe von Enten zu verlassen. Tipphinweise sind relativ neu und die Jury ist sich noch nicht sicher, wann sie die pythonischste Lösung sind. Ein relativ unumstrittener, aber sehr allgemeiner Vergleich: Typhinweise bieten eine Form der Dokumentation, die erzwungen werden kann, es Code ermöglicht, frühere und leichter verständliche Fehler zu generieren, Fehler abfangen kann, die bei der Eingabe von Enten nicht möglich sind, und statisch überprüft werden kann (in ungewöhnlichen Fällen) Sinn, aber es ist immer noch außerhalb der Laufzeit). Auf der anderen Seite war das Tippen von Enten lange Zeit der pythonische Weg, erlegt nicht den kognitiven Aufwand für statisches Tippen auf, ist weniger ausführlich und akzeptiert alle brauchbaren Typen und noch einige mehr.


2
-1: mypy nennt sich speziell "statische Typprüfung", daher bin ich mir nicht sicher, woher Sie "Typprüfung muss zur Laufzeit durchgeführt werden" haben.
Kevin

@ Kevin Im Nachhinein war das ein unnötiger Exkurs, aber um mehr darüber zu erfahren, werden Pythons Typhinweise in Laufzeitdaten umgewandelt und mypysind ein Python-Modul, mit importlibdem auf diese Daten zugegriffen wird . Ob dies eine "statische Typprüfung" ist, ist eine philosophische Frage, unterscheidet sich jedoch von den meisten Erwartungen, da der normale Sprachinterpreter und die Importmaschinerie beteiligt sind.
Praxeolitic

4
Das stimmt auch nicht. Es verwendet typed_ast, das selbst nur ein Klon von ast mit zusätzlichen Funktionen ist. ast importiert keine Module; Es analysiert sie in einen abstrakten Syntaxbaum.
Kevin

18

Hier ist ein Beispiel, warum das Tippen von Enten böse ist, ohne zu wissen, wann es gefährlich ist. Zum Beispiel: Hier ist der Python-Code (möglicherweise ohne richtiges Einrücken). Beachten Sie, dass diese Situation vermieden werden kann, indem Sie sich um die Instanz und die Unterklasse der Funktionen kümmern, um sicherzustellen, dass Sie keine Bombe bekommen, wenn Sie wirklich eine Ente brauchen.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()

36
Selbst bei der Typprüfung können Sie ein class EvilDuck(Duck)talk erstellen und überschreiben (). Oder eher class ChineseCancerDuck(Duck)mit einer bösen Nebenwirkung, die erst Jahre später auftritt. Sie sollten Ihr Kind besser beaufsichtigen (und ihr Spielzeug gründlich testen :)
Brett Thomas,

36
Bomben sprechen nicht. Fügen Sie keine unsinnigen Methoden hinzu, und dies wird nicht passieren.
Rechtsfalte

7
@Dmitry, dies ist die übliche Kritik an Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... Sie sagen im Grunde, dass jede Schnittstelle, für die die Semantik nicht durch die Sprache erzwungen wird, böse ist. Ich glaube, das ist eher der Ansatz von Java. Der springende Punkt bei Pythons Ententypisierung ist, dass sie nur funktioniert, wenn es eine allgemein anerkannte Konvention darüber gibt, was bestimmte Schnittstellen bedeuten. Zum Beispiel könnten Sie viel Python-Code borken, indem Sie das __file__Attribut (das häufig zur Identifizierung dateiähnlicher Objekte verwendet wird) überschreiben , um etwas anderes zu bedeuten.
Dan Lenski

2
Das alles läuft auf den alten Witz "Doktor, es tut weh, wenn ich das tue." ... "Dann tu das nicht." Unbefriedigend für jemanden, der es gewohnt ist, "wenn es kompiliert, läuft es", aber aus diesem Grund ist das Testen der Besessenheit aus der dynamischen Sprachwelt hervorgegangen.
Clacke

1
@clacke Grundsätzlich ist es zu teuer, Typen zur Laufzeit strikt durchzusetzen, da ALLES ein Objekt sein muss (um von einem String auf einen beliebigen Typ abzubilden), und zu bequem, um kein Ducktyping zu haben, da Ducktyping wirklich leistungsstarke Prototyping-Techniken ermöglicht, die Dinge überwinden, die das überwinden sind normalerweise sehr schwierig mit starren Schnittstellen zu tun. Außerdem steht jede statische Sprache vor einem Punkt, an dem Ententypisierung über dynamische Bibliotheken, Auswertung und Stringifizierung oder Schnittstellen erstellt werden muss, und diese Dinge machen sie nicht von Natur aus böse, sondern nur sehr mächtig.
Dmitry

12
isinstance(o, str)

Link zu Dokumenten


1
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
EKons

7

Ich denke, das Coole an einer dynamischen Sprache wie Python ist, dass man so etwas wirklich nicht überprüfen muss.

Ich würde einfach die erforderlichen Methoden für Ihr Objekt aufrufen und eine abfangen AttributeError. Später können Sie Ihre Methoden mit anderen (scheinbar nicht verwandten) Objekten aufrufen, um andere Aufgaben auszuführen, z. B. das Verspotten eines Objekts zum Testen.

Ich habe dies oft verwendet, um Daten aus dem Web zu holen, mit urllib2.urlopen()denen ein dateiähnliches Objekt zurückgegeben wird. Dies kann wiederum an fast jede Methode übergeben werden, die aus einer Datei liest, da dieselbe read()Methode wie eine echte Datei implementiert wird .

Aber ich bin mir sicher, dass es eine Zeit und einen Ort für die Nutzung gibt isinstance(), sonst wäre es wahrscheinlich nicht da :)


Ein gutes Beispiel dafür, wann Sie es verwenden müssen , ist das Parsen eines dynamischen JSON-Objekts. Sie wissen nicht im Voraus, ob ein Feld eine Zeichenfolge oder ein Wörterbuch ist.
Grau

6

Für komplexere Typüberprüfungen gefällt mir der Ansatz von typeguard , anhand von Anmerkungen zu Python-Typ-Hinweisen zu validieren:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Sie können sehr komplexe Validierungen auf sehr saubere und lesbare Weise durchführen.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 

6

Sie können den Typ einer Variablen mit __name__ eines Typs überprüfen.

Ex:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'

Vielen Dank, dies ist der Geheimcode, den ich wollte, als ich ihn dem Benutzer als Feedback anzeigte. Ich habe zu lange
gebraucht

5

Zu Hugo:

Du meinst wahrscheinlich listeher alsarray , aber das weist auf das gesamte Problem bei der Typprüfung hin - Sie möchten nicht wissen, ob es sich bei dem fraglichen Objekt um eine Liste handelt, Sie möchten wissen, ob es sich um eine Sequenz handelt oder ob es sich um ein einzelnes Objekt handelt. Versuchen Sie also, es wie eine Sequenz zu verwenden.

Angenommen, Sie möchten das Objekt einer vorhandenen Sequenz hinzufügen, oder wenn es sich um eine Sequenz von Objekten handelt, fügen Sie alle hinzu

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Ein Trick dabei ist, wenn Sie mit Zeichenfolgen und / oder Zeichenfolgenfolgen arbeiten - das ist schwierig, da eine Zeichenfolge oft als einzelnes Objekt betrachtet wird, aber auch als Zeichenfolge. Schlimmer noch, da es sich wirklich um eine Folge von Strings mit einfacher Länge handelt.

Normalerweise entwerfe ich meine API so, dass sie nur einen einzelnen Wert oder eine Sequenz akzeptiert - das erleichtert die Arbeit. Es ist nicht schwer, einen [ ]Wert um Ihren einzelnen Wert zu setzen, wenn Sie ihn bei Bedarf übergeben.

(Dies kann jedoch zu Fehlern bei Zeichenfolgen führen, da diese wie Sequenzen aussehen.)


0

Eine einfache Möglichkeit, den Typ zu überprüfen, besteht darin, ihn mit etwas zu vergleichen, dessen Typ Sie kennen.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True


-7

Sie können mit der folgenden Zeile überprüfen, welcher Zeichentyp der angegebene Wert ist:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)

3
Sind Sie sicher, dass Sie diese Antwort @Venkatesan nicht löschen möchten?
Grau
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.