Testen Sie, ob eine Variable eine Liste oder ein Tupel ist

234

Wie kann man in Python am besten testen, ob eine Variable eine Liste oder ein Tupel enthält? (dh eine Sammlung)

Ist isinstance()so böse wie hier vorgeschlagen? http://www.canonical.org/~kragen/isinstance/

Update: Der häufigste Grund, warum ich eine Liste von einer Zeichenfolge unterscheiden möchte, ist, wenn ich eine unbegrenzt tief verschachtelte Baum- / Datenstruktur von Listen mit Listen von Listen mit Zeichenfolgen usw. habe, die ich mit einem rekursiven Algorithmus untersuche und die ich benötige zu wissen, wann ich die "Blatt" -Knoten getroffen habe.

Interstar
quelle
72
Die Typprüfung allgemein als böse abzulehnen, ist ein bisschen einfach. Es ist Teil der Sprache. Wenn es so böse ist, sollte jemand ein PEP schreiben, um es zu entfernen.
Adam Crossland
4
@ Adam Crossland: "Es ist Teil der Sprache." Genau wie die Division durch Null. Es ist vermeidbar. In diesem Fall ist es ohne zusätzliche Informationen wahrscheinlich völlig unnötig. Die meisten Typprüfungen in Python sind unnötig. Da nicht alles unnötig ist, muss eine Typprüfung vorhanden sein. Das heißt aber nicht, dass es hilfreich, wertvoll oder sogar eine gute Idee ist.
S.Lott
11
Sie sagen also, dass eine Typprüfung erforderlich ist, aber trotzdem ist es nutzlos, wertlos und eine schlechte Idee. Entschuldigung, das macht einfach keinen Sinn.
Glenn Maynard
18
Das "XXX ist böse" ist schlecht durchdacht, eine irreführende Abkürzung für "die Art und Weise, wie Sie XXX fordern, deutet darauf hin, dass Sie nicht verstehen, wann es tatsächlich verwendet werden sollte, und Sie wollen mit ziemlicher Sicherheit etwas anderes". Das ist hier höchstwahrscheinlich der Fall.
Glenn Maynard
2
Ich habe es nicht allgemein als böse abgetan. Ich habe einen kurzen Aufsatz darüber geschrieben, wann es böse ist und wann es gerechtfertigt ist. Dieser Aufsatz mag viele Dinge sein - richtig, falsch, klar, vage, erfreulich, langweilig - aber eine Sache, die es nicht ist, ist eine breite Ablehnung der Technik.
Kragen Javier Sitaker

Antworten:

101

Gehen Sie voran und verwenden isinstanceSie, wenn Sie es brauchen. Es ist etwas böse, da es benutzerdefinierte Sequenzen, Iteratoren und andere Dinge ausschließt, die Sie möglicherweise tatsächlich benötigen. Manchmal müssen Sie sich jedoch anders verhalten, wenn beispielsweise jemand eine Zeichenfolge übergibt. Meine Präferenz wäre es, explizit nach stroder zu suchenunicode mögen:

import types
isinstance(var, types.StringTypes)

NB Verwechseln Sie nicht types.StringTypemittypes.StringTypes . Letzteres beinhaltet strund unicodeObjekte.

Das typesModul wird von vielen als veraltet angesehen, da es nur direkt mit dem Objekttyp verglichen werden soll. Wenn Sie das oben Gesagte also lieber nicht verwenden möchten, können Sie alternativ explizit gegen strund prüfenunicode wie :

isinstance(var, (str, unicode)):

Bearbeiten:

Besser noch ist:

isinstance(var, basestring)

Bearbeitung beenden

Nach beiden können Sie sich wieder so verhalten, als ob Sie eine normale Sequenz erhalten, und Nicht-Sequenzen entsprechende Ausnahmen auslösen lassen.

Das "Böse" an der Typprüfung ist nicht, dass Sie sich für einen bestimmten Objekttyp möglicherweise anders verhalten möchten, sondern dass Sie Ihre Funktion künstlich daran hindern, das Richtige mit unerwarteten Objekttypen zu tun, die sonst das Richtige tun würden. Wenn Sie einen endgültigen Fallback haben, der nicht typgeprüft ist, entfernen Sie diese Einschränkung. Es sollte beachtet werden, dass zu viel Typprüfung ein Codegeruch ist, der darauf hinweist, dass Sie möglicherweise ein Refactoring durchführen möchten, aber das bedeutet nicht unbedingt, dass Sie es aus dem Getgo heraus vermeiden sollten.

jcdyer
quelle
2
Das Typmodul ist ein historisches Artefakt. Wie unter docs.python.org/dev/library/types.html#module-types erwähnt, sollten Sie diesen strTyp direkt verwenden, wenn Sie wirklich nach dem Typ suchen müssen , anstatt types.StringTypenur einen Alias ​​dafür zu verwenden. Aber ich glaube nicht, dass diese Antwort die gestellte Frage beantwortet, denn es ging um "eine Sammlung". Es sei denn, Sie verwenden eine Python, die neu genug ist, um das abcModul zu haben , nach dem Sie nicht suchen können isinstance, und selbst dann würde ich empfehlen, die Prüfung nach Möglichkeit zu vermeiden.
Mzz
1
assert isinstance(u'abc', str) == False. Ich bin damit einverstanden, dass es besser ist, direkt mit dem Typ zu vergleichen, als das typesModul zu verwenden, aber types.StringTypesetwas tut, das strdies nicht tut: Es gibt True für strund unicodeObjekte zurück. Ich werde meine Antwort bearbeiten, um alternativ eine doppelte Überprüfung anzubieten.
JCDyer
1
Mir ist klar, dass ich die Frage der direkten Suche nach Sammlungen nicht beantwortet habe, aber die eigentliche Frage lautete: "Ist isinstanceböse?" Und ich habe ein Gegenbeispiel gegeben, das (1) eine nicht böse Verwendung ist isinstance, weil ein Fallback bedeutet, dass das Ententyping nicht unterbrochen wird, und (2) eine gute Lösung für eine sehr häufige Motivation ist, die Menschen haben, um zu überprüfen, ob etwas ist ein listoder tuple(dh um sie von Strings zu unterscheiden).
JCDyer
Ich stimme der Einschränkung zu, dass es für benutzerdefinierte Typen oft nützlich ist, sich auch wie Zeichenfolgen zu verhalten. Aber Pythons OO geht nur so weit ...
Kragen Javier Sitaker
Macht class Foo(str): passwas du willst
JCDyer
567
if type(x) is list:
    print 'a list'
elif type(x) is tuple:
    print 'a tuple'
else:
    print 'neither a tuple or a list'
Wall-E
quelle
1
Scheint nicht zu funktionieren: type ([]) ==> list; Typ ([]) ist Liste ===> False
sten
3
In Python 2.7.5: type([]) is listkehrt zurückTrue
David Geiger
54
type(x) in [list,tuple]ist kürzer.
Alex Holcombe
wenn x und Typ (x) Liste ist: um die [] Nichtübereinstimmung zu vermeiden
Kenichi Shibata
Ich musste so viel nach unten scrollen. : D
Rithwik
38

Es ist nichts falsch daran zu benutzen isinstance , solange es nicht redundant ist. Wenn eine Variable nur eine Liste / ein Tupel sein soll, dokumentieren Sie die Schnittstelle und verwenden Sie sie einfach als solche. Ansonsten ist eine Überprüfung durchaus sinnvoll:

if isinstance(a, collections.Iterable):
    # use as a container
else:
    # not a container!

Diese Art der Prüfung hat einige gute Anwendungsfälle, z. B. mit der Standardzeichenfolge Start mit / Ende mit (obwohl diese zur Genauigkeit in C in CPython mithilfe einer expliziten Überprüfung implementiert werden, um , ob es sich um ein Tupel handelt - es gibt mehrere Möglichkeiten um dieses Problem zu lösen, wie in dem Artikel erwähnt, auf den Sie verlinken).

Eine explizite Überprüfung ist oft besser als der Versuch, das Objekt als Container zu verwenden und die Ausnahme zu behandeln. Dies kann zu allen möglichen Problemen führen, wenn Code teilweise oder unnötig ausgeführt wird.

Scott Griffiths
quelle
15
Dies ist eine gute Möglichkeit, um zu überprüfen, ob eine Variable iterierbar ist. Für die Zwecke dieser Frage wird es jedoch wahrscheinlich nicht funktionieren. Beachten Sie, dass eine Zeichenfolge auch iterierbar ist und wahrscheinlich ein falsches Positiv erzeugen würde.
Corey O.
Ein setObjekt ist auch iterierbar, was bedeutet, dass Sie zwar Elemente daraus entfernen können, aber keine bestimmte Reihenfolge garantieren, was für bestimmte Algorithmen sehr gefährlich ist. In den Fällen, in denen die Reihenfolge der Elemente von Bedeutung ist, kann ein Algorithmus, der dieses Snippet verwendet, möglicherweise unterschiedliche Ergebnisse bei unterschiedlichen Läufen generieren!
Koo
14

Dokumentieren Sie das Argument als eine Sequenz und verwenden Sie es als Sequenz. Überprüfen Sie nicht den Typ.

Ignacio Vazquez-Abrams
quelle
12

Wie wäre es mit : hasattr(a, "__iter__")?

Es zeigt an, ob das zurückgegebene Objekt als Generator durchlaufen werden kann. Standardmäßig können Tupel und Listen, aber nicht die Zeichenfolgentypen.

user1914881
quelle
Ich fand das wirklich nützlich.
Javadba
8
Erzielt auch True für Zeichenfolgen (zumindest in Python 3).
SzieberthAdam
2
Das ist eine falsche Antwort. Da Typ 'str' auch die Methode ' iter ' hat. @ SzieberthAdam hatte recht. Der Typ 'set' ist ebenfalls iterierbar, aber nicht bestellbar.
PADYMKO
Auch Diktate haben __iter__.
Thet
Wenn Sie fortfahren, kann jeder benutzerdefinierte Typ __iter__aus irgendeinem Grund ...
Alexander Irbis
10

Bei Python 2.8- type(list) is listRückgaben false
würde ich vorschlagen, den Typ auf diese schreckliche Weise zu vergleichen:

if type(a) == type([]) :
  print "variable a is a list"

(Nun, zumindest auf meinem System, mit Anaconda unter Mac OS X Yosemite)

Xanderite
quelle
1
Wird Typ (a) is list auch als false ausgewertet?
Dmitriy Sintsov
4
Meinten Sie Python 2.7.8? python.org/dev/peps/pep-0404/#official-pronouncement
Carl
Hallo, ich bin neugierig: Warum halten Sie Ihr Beispiel für "schrecklich"?
Seth Connell
type(list) is listkehrt zurück, Falseweil type(list)es typenicht ist list. type(list()) is listoder mit einer anderen Instanz einer Liste wird zurückgegeben True.
Michael Greene
8

Python verwendet "Duck Typing", dh wenn eine Variable wie eine Ente kakaks, muss es eine Ente sein. In Ihrem Fall möchten Sie wahrscheinlich, dass es iterierbar ist, oder Sie möchten über einen bestimmten Index auf das Element zugreifen. Sie sollten dies einfach tun: dh das Objekt in for var:oder var[idx]innerhalb eines tryBlocks verwenden, und wenn Sie eine Ausnahme erhalten, war es keine Ente ...

Wim
quelle
7
Das Problem dabei ist, ob vareine String-Iteration mit wahrscheinlich unerwarteten Ergebnissen auftritt.
Brian M. Hunt
Trotz der von Brian M. Hunt angegebenen Tatsache ist seine Lösung eine ziemlich pythonische Lösung, wenn es darum geht, eher um Vergebung als um Erlaubnis zu bitten.
Géza Török
6
>>> l = []
>>> l.__class__.__name__ in ('list', 'tuple')
True
tetra5
quelle
3

Wenn Sie nur wissen müssen, ob Sie die foo[123]Notation mit der Variablen verwenden können, können Sie überprüfen , ob ein __getitem__Attribut vorhanden ist (das Python beim Zugriff per Index aufruft)hasattr(foo, '__getitem__')

Geoff Reedy
quelle
2

Muss ein komplexerer Test sein, wenn Sie wirklich so ziemlich alles als Funktionsargument behandeln wollen.

type(a) != type('') and hasattr(a, "__iter__")

Normalerweise reicht es jedoch aus, nur zu formulieren, dass eine Funktion iterabel erwartet, und dann nur zu überprüfen type(a) != type('') .

Es kann auch vorkommen, dass Sie für eine Zeichenfolge einen einfachen Verarbeitungspfad haben oder dass Sie nett sind und einen Split usw. durchführen. Sie möchten also keine Zeichenfolgen anschreien, und wenn Ihnen jemand etwas Seltsames schickt, lassen Sie es ihn einfach haben eine Ausnahme.

ZXX
quelle
2

Eine andere einfache Möglichkeit, herauszufinden, ob eine Variable entweder eine Liste oder ein Tupel ist oder im Allgemeinen den Variablentyp zu überprüfen, ist:

    def islist(obj):

        if ("list" in str(type(obj)) ): return True

        else : return False
Mahdi Omidiyan
quelle
1

Im Prinzip stimme ich Ignacio oben zu, aber Sie können auch den Typ verwenden , um zu überprüfen, ob etwas ein Tupel oder eine Liste ist.

>>> a = (1,)
>>> type(a)
(type 'tuple')
>>> a = [1]
>>> type(a)
(type 'list')
Adam Crossland
quelle