Dies ist, was ich normalerweise mache, um sicherzustellen, dass die Eingabe a list
/ tuple
- aber nicht a ist str
. Weil ich oft auf Fehler gestoßen bin, bei denen eine Funktion versehentlich ein str
Objekt passiert , und die Zielfunktion for x in lst
davon ausgeht, dass lst
es sich tatsächlich um ein list
oder handelt tuple
.
assert isinstance(lst, (list, tuple))
Meine Frage ist: Gibt es einen besseren Weg, dies zu erreichen?
Antworten:
Nur in Python 2 (nicht in Python 3):
Ist eigentlich das, was Sie wollen, sonst verpassen Sie viele Dinge, die sich wie Listen verhalten, aber keine Unterklassen von
list
oder sindtuple
.quelle
basestring
ist weg, und Sie suchen nur nachisinstance(lst, str)
.set
Generatorausdrücke, Iteratoren. Es gibt exotische Dinge wiemmap
weniger exotische Dinge,array
die sich wie Listen verhalten, und wahrscheinlich noch viel mehr, das ich vergessen habe.lst
es iterierbar ist, während das Original dies tat (z. B. ein Int würde diese Prüfung bestehen)assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
Denken Sie daran, dass wir in Python "Duck Typing" verwenden möchten. Alles, was sich wie eine Liste verhält, kann als Liste behandelt werden. Überprüfen Sie also nicht den Typ einer Liste, sondern prüfen Sie, ob sie sich wie eine Liste verhält.
Aber Strings wirken auch wie eine Liste, und oft ist das nicht das, was wir wollen. Es gibt Zeiten, in denen es sogar ein Problem ist! Suchen Sie also explizit nach einer Zeichenfolge, verwenden Sie dann jedoch die Ententypisierung.
Hier ist eine Funktion, die ich zum Spaß geschrieben habe. Es ist eine spezielle Version
repr()
, die jede Sequenz in spitzen Klammern ('<', '>') druckt.Dies ist insgesamt sauber und elegant. Aber was macht dieser
isinstance()
Scheck dort? Das ist eine Art Hack. Aber es ist wichtig.Diese Funktion ruft sich rekursiv für alles auf, was sich wie eine Liste verhält. Wenn wir die Zeichenfolge nicht speziell behandeln würden, würde sie wie eine Liste behandelt und jeweils ein Zeichen aufgeteilt. Aber dann würde der rekursive Aufruf versuchen, jedes Zeichen als Liste zu behandeln - und es würde funktionieren! Sogar eine einstellige Zeichenfolge funktioniert als Liste! Die Funktion ruft sich selbst rekursiv auf, bis der Stapel überläuft.
Funktionen wie diese, die von jedem rekursiven Aufruf abhängen, der die zu erledigende Arbeit aufschlüsselt, müssen Sonderzeichenfolgen enthalten - da Sie eine Zeichenfolge nicht unter der Ebene einer Zeichenfolge mit einem Zeichen und sogar einer Zeichenfolge aufteilen können -character string verhält sich wie eine Liste.
Hinweis: Das
try
/except
ist der sauberste Weg, um unsere Absichten auszudrücken. Wenn dieser Code jedoch zeitkritisch wäre, möchten wir ihn möglicherweise durch eine Art Test ersetzen, um festzustellen, obarg
es sich um eine Sequenz handelt. Anstatt den Typ zu testen, sollten wir wahrscheinlich das Verhalten testen. Wenn es eine.strip()
Methode hat, ist es eine Zeichenfolge. Betrachten Sie sie also nicht als Sequenz. Andernfalls ist es eine Sequenz, wenn es indizierbar oder iterierbar ist:BEARBEITEN: Ich habe das Obige ursprünglich mit einem Check für geschrieben,
__getslice__()
aber ich habe festgestellt, dass in dercollections
Moduldokumentation die interessante Methode ist__getitem__()
; Das macht Sinn, so indizieren Sie ein Objekt. Das scheint grundlegender zu sein,__getslice__()
also habe ich das oben Gesagte geändert.quelle
srepr
ist eine sehr interessante Idee. Aber ich bin anderer Meinung als Sie, ob es sich um einen Sonderfall handeltstr
. Ja,str
ist bei weitem die offensichtlichste und häufigste iterable, die eine unendliche Rekursion in verursachen würdesrepr
. Aber ich kann mir leicht benutzerdefinierte Iterables vorstellen, die sich gleich verhalten (mit oder ohne guten Grund). Anstelle eines Sonderfallsstr
sollten wir zugeben, dass dieser Ansatz in eine unendliche Rekursion geraten kann, und einer Art und Weise zustimmen, wie wir damit umgehen. Ich werde meinen Vorschlag in einer Antwort veröffentlichen.srepr()
ist in Ordnung, wie es ist. Wir brauchen eine rekursive Funktion, um Dinge wie eine Liste zu behandeln, die in einer anderen Liste verschachtelt ist. aber für Zeichenketten würden wir sie lieber als"foo"
als drucken lassen<'f', 'o', 'o'>
. Eine explizite Prüfung auf einen String ist hier also sinnvoll. Es gibt auch keine anderen Beispiele für Datentypen, bei denen das Iterieren immer eine Iterierbarkeit zurückgibt und die Rekursion immer einen Stapelüberlauf verursacht. Daher benötigen wir keine spezielle Eigenschaft, um dies zu testen ("Praktikabilität schlägt Reinheit").__iter__()
Methode in Python 3 haben, aber nicht in Python 2. Sie vermissen Klammern inis_sequence()
, es sollte lauten:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
quelle
if isinstance( H, (list, tuple) ): ...
ist jedoch kürzer und klarer.if type(H) in [list, tuple]:
Für Python 3:
Für Python 2:
quelle
collections.Sequence
aber ich habe es getestet und sehe, dass sie es tun. Das tut es auchxrange
. Noch besser ist, dass dieser Test ausschließtdict
, der sowohl__getitem__
als auch hat__iter__
.inspect.getmro(list)
nicht enthältSequence
? Wie sollen wir herausfinden, was wir tun können,isinstance
wenngetmro
nicht alles gezeigt wird?Sequence
ist eine abstrakte Klasse.Python mit PHP-Geschmack:
quelle
__getitem__
. Auch der Name ist irreführend, da es auch ein Array-Modul gibt. Und die var könnte auch eine numpy.ndarray oder ein anderer Typ sein, der hat__getitem__
. Die richtige Antwort finden Sie unter stackoverflow.com/a/1835259/470560 .str
hat auch__getitem__
deshalb Ihren Scheck nicht ausschließenstr
__getitem__
ist hier ein schlechter Rat.Im Allgemeinen ist die Tatsache, dass eine Funktion, die über ein Objekt iteriert, sowohl für Zeichenfolgen als auch für Tupel und Listen funktioniert, mehr eine Funktion als ein Fehler. Sie sicherlich können verwenden
isinstance
oder Ente eingeben ein Argument zu überprüfen, aber warum sollten Sie?Das klingt nach einer rhetorischen Frage, ist es aber nicht. Die Antwort auf "Warum sollte ich den Typ des Arguments überprüfen?" wird wahrscheinlich eine Lösung für das eigentliche Problem vorschlagen, nicht für das wahrgenommene Problem. Warum ist es ein Fehler, wenn eine Zeichenfolge an die Funktion übergeben wird? Außerdem: Wenn es ein Fehler ist, wenn eine Zeichenfolge an diese Funktion übergeben wird, ist es auch ein Fehler, wenn eine andere iterierbare Nicht-Liste / Tupel an diese Funktion übergeben wird? Warum oder warum nicht?
Ich denke, dass die häufigste Antwort auf die Frage wahrscheinlich ist, dass Entwickler, die schreiben,
f("abc")
erwarten , dass sich die Funktion so verhält, als hätten sie geschriebenf(["abc"])
. Es gibt wahrscheinlich Umstände, in denen es sinnvoller ist, Entwickler vor sich selbst zu schützen, als den Anwendungsfall des Iterierens über die Zeichen in einer Zeichenfolge zu unterstützen. Aber ich würde zuerst lange und gründlich darüber nachdenken.quelle
Versuchen Sie dies für die Lesbarkeit und Best Practices:
Python2
Python3
Ich hoffe es hilft.
quelle
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
->isinstance([1, 2, 3], List
=True
undisinstance("asd", List)
=False
Das
str
Objekt hat kein__iter__
AttributSo können Sie eine Überprüfung durchführen
und dies wird auch
AssertionError
für jedes andere nicht iterierbare Objekt ein Nettes auslösen.Bearbeiten: Wie Tim in den Kommentaren erwähnt, funktioniert dies nur in Python 2.x, nicht in 3.x.
quelle
hasattr('','__iter__')
zurückgegebenTrue
. Und das ist natürlich sinnvoll, da Sie über eine Zeichenfolge iterieren können.Dies ist nicht dazu gedacht, das OP direkt zu beantworten, aber ich wollte einige verwandte Ideen teilen.
Ich war sehr interessiert an der obigen Antwort von @steveha, die ein Beispiel zu geben schien, bei dem das Tippen von Enten zu brechen scheint. Beim zweiten Gedanken deutet sein Beispiel jedoch darauf hin, dass es schwierig ist, sich an die Ententypisierung zu halten, aber es deutet nicht darauf hin, dass dies
str
eine besondere Behandlung verdient.Schließlich kann ein Nicht-
str
Typ (z. B. ein benutzerdefinierter Typ, der einige komplizierte rekursive Strukturen beibehält) dazu führen, dass die @ steveha-srepr
Funktion eine unendliche Rekursion verursacht. Dies ist zwar zugegebenermaßen eher unwahrscheinlich, wir können diese Möglichkeit jedoch nicht ignorieren. Daher eher als Sonder-Gehäusestr
insrepr
, sollten wir klären , was wir wollen ,srepr
wenn ein unendliches Rekursion Ergebnisse tun.Es scheint, dass ein vernünftiger Ansatz darin besteht, die Rekursion im
srepr
Moment einfach zu brechenlist(arg) == [arg]
. Dies würde in der Tat vollständig löst das Problem mitstr
, ohneisinstance
.Eine wirklich komplizierte rekursive Struktur kann jedoch eine Endlosschleife verursachen, in der dies
list(arg) == [arg]
niemals geschieht. Daher ist die obige Prüfung zwar nützlich, aber nicht ausreichend. Wir brauchen so etwas wie eine harte Grenze für die Rekursionstiefe.Mein Punkt ist, dass, wenn Sie vorhaben, mit beliebigen Argumenttypen umzugehen, die Handhabung
str
über die Ententypisierung weitaus einfacher ist als die Behandlung der allgemeineren Typen, denen Sie (theoretisch) begegnen können. Wenn Sie also das Bedürfnis haben,str
Instanzen auszuschließen , sollten Sie stattdessen verlangen, dass das Argument eine Instanz eines der wenigen Typen ist, die Sie explizit angeben.quelle
str
den der Sonderfallcode behandelt. Aber vielleicht sollte es eine neue Standardeigenschaft geben, die der Code beispielsweise untersuchen kann.__atomic__
und die signalisiert, dass etwas nicht weiter aufgeschlüsselt werden kann. Es ist wahrscheinlich zu spät, umatomic()
Python eine weitere integrierte Funktion hinzuzufügen , aber vielleicht können wirfrom collections import atomic
etwas hinzufügen oder so.Ich finde eine solche Funktion namens is_sequence im Tensorflow .
Und ich habe überprüft, ob es Ihren Anforderungen entspricht.
quelle
Ich mache das in meinen Testfällen.
Ungetestet an Generatoren, denke ich, bleiben Sie bei der nächsten "Ausbeute", wenn Sie in einen Generator geleitet werden, was die Dinge stromabwärts vermasseln kann. Aber andererseits ist dies ein "unittest"
quelle
Wie wäre es mit "Ententyping"?
oder
beziehungsweise. Dies vermeidet das
isinstance
/hasattr
Introspection-Zeug.Sie können auch umgekehrt überprüfen:
Alle Varianten ändern den Inhalt der Variablen nicht, sondern implizieren eine Neuzuweisung. Ich bin mir nicht sicher, ob dies unter bestimmten Umständen unerwünscht sein könnte.
Interessanterweise mit der „in place“ Zuordnung
+=
nichtTypeError
würde auf jedem Fall erhöht werden , wennlst
a Liste (kein Tupel ). Deshalb erfolgt die Zuordnung auf diese Weise. Vielleicht kann jemand Licht ins Dunkel bringen, warum das so ist.quelle
einfachster Weg ... mit
any
undisinstance
quelle
Eine andere Version der Ententypisierung, um stringähnliche Objekte von anderen sequenzähnlichen Objekten zu unterscheiden.
Die Zeichenfolgendarstellung von Zeichenfolgen-ähnlichen Objekten ist die Zeichenfolge selbst. Sie können also überprüfen, ob Sie vom
str
Konstruktor ein gleiches Objekt zurückerhalten :Dies sollte für alle Objekte funktionieren, die mit
str
und für alle Arten von iterierbaren Objekten kompatibel sind.quelle
Python 3 hat Folgendes:
Um nach Listen und Tupeln zu suchen, wäre es:
quelle
quelle
Mach das einfach
quelle
in Python> 3.6
quelle
str
, keine Zeichenfolge. Versuchenisinstance('my_string', collections.abc.Container)
Sie es und Sie werden sehen, dass es zurückkehren wirdTrue
. Dies liegt daranabc.Container
, dass die__contains__
Methode bereitgestellt wird und die Zeichenfolgen sie natürlich haben.Ich neige dazu (wenn ich wirklich, wirklich musste):
quelle
some_var
eine Instanz einer Klasse bin , zu der eine Unterklasse gehörtlist()
? Ihr Code hat keine Ahnung, was er damit machen soll, obwohl er unter dem Code "Mach etwas mit einer Liste" perfekt funktioniert. Und Sie müssen sich selten um den Unterschied zwischen einer Liste und einem Tupel kümmern. Entschuldigung, -1.type(tuple())
- reicht austuple
. Gleiches gilt für die Liste. Außerdem beidesstr
undunicode
verlängernbasestring
, was der eigentliche Zeichenfolgentyp ist, also möchten Sie stattdessen danach suchen.type(i) is list
. Auchtype(list())
ist nur sichlist
selbst ... Schließlich funktioniert dies nicht ordnungsgemäß mit Unterklassen. Wenni
es sich tatsächlich um OrderedDict oder eine Art NamedTuple handelt, wird dieser Code als Zeichenfolge behandelt.