__getitem__ist auch ausreichend, um ein Objekt iterierbar zu machen
Kos
4
FWIW: ist iter(myObj)erfolgreich isinstance(myObj, dict), wenn Sie myObjalso in beiden Fällen erfolgreich sind , wenn Sie sich eine Sequenz von dicts oder eine einzelne ansehen dict. Eine Subtilität, die wichtig ist, wenn Sie wissen möchten, was eine Sequenz ist und was nicht. (in Python 2)
Ben Mosher
7
__getitem__ist auch ausreichend, um ein Objekt iterierbar zu machen ... wenn es bei Null beginnt .
Carlos A. Gómez
Antworten:
26
Ich habe dieses Problem in letzter Zeit ziemlich viel untersucht. Aufgrund dessen komme ich zu dem Schluss, dass dies heutzutage der beste Ansatz ist:
from collections.abc importIterable# drop `.abc` with Python 2.7 or lowerdef iterable(obj):return isinstance(obj,Iterable)
Das oben Gesagte wurde bereits früher empfohlen, aber der allgemeine Konsens war, dass die Verwendung iter()besser wäre:
Wir haben iter()unseren Code auch für diesen Zweck verwendet, aber in letzter Zeit ärgere ich mich immer mehr über Objekte, die nur __getitem__als iterierbar angesehen wurden. Es gibt triftige Gründe für __getitem__ein nicht iterierbares Objekt, und mit ihnen funktioniert der obige Code nicht gut. Als Beispiel aus dem wirklichen Leben können wir Faker verwenden . Der obige Code gibt an, dass es iterierbar ist, aber der Versuch, es zu iterieren, führt zu einem AttributeError(getestet mit Faker 4.0.2):
>>>from faker importFaker>>> fake =Faker()>>> iter(fake)# No exception, must be iterable<iterator object at 0x7f1c71db58d0>>>> list(fake)# OoopsTraceback(most recent call last):File"<stdin>", line 1,in<module>File"/home/.../site-packages/faker/proxy.py", line 59,in __getitem__
return self._factory_map[locale.replace('-','_')]AttributeError:'int' object has no attribute 'replace'
Wenn wir verwenden würden insinstance(), würden wir Faker-Instanzen (oder andere Objekte, die nur __getitem__) nicht versehentlich als iterierbar betrachten:
In früheren Antworten wurde darauf hingewiesen, dass die Verwendung iter()sicherer ist, da die alte Methode zur Implementierung der Iteration in Python darauf basiert __getitem__und der isinstance()Ansatz dies nicht erkennen würde. Dies mag bei alten Python-Versionen der Fall gewesen sein, aber basierend auf meinen ziemlich umfassenden Tests isinstance()funktioniert es heutzutage großartig. Der einzige Fall, in dem isinstance()dies nicht funktioniert iter()hat, war die UserDictVerwendung von Python 2. Wenn dies relevant ist, können Sie es verwenden isinstance(item, (Iterable, UserDict)), um dies zu behandeln.
Wird auch typing.Dictals iterierbar angesehen iter(Dict), list(Dict)schlägt aber mit Fehler fehl TypeError: Parameters to generic types must be types. Got 0.. Wie erwartet wird isinstance(Dict, Iterable)false zurückgegeben.
Pekka Klärck
1
Ich bin zu dem gleichen Schluss gekommen, aber aus verschiedenen Gründen. Durch iterdie Verwendung wurde ein Teil unseres Codes, der "Pre-Caching" verwendete, unnötig verlangsamt. Wenn der __iter__Code langsam ist, wird dies auch der iterFall sein ... jederzeit, wenn Sie nur sehen möchten, ob etwas iterierbar ist.
Thorwhalen
842
Das Suchen nach Funktionen für __iter__Sequenztypen würde jedoch beispielsweise bei Zeichenfolgen in Python 2 fehlschlagen . Ich würde auch gerne die richtige Antwort wissen, bis dahin gibt es hier eine Möglichkeit (die auch bei Strings funktionieren würde):
from __future__ import print_function
try:
some_object_iterator = iter(some_object)exceptTypeErroras te:print(some_object,'is not iterable')
Das itereingebaute prüft die __iter__Methode oder bei Strings die __getitem__Methode.
Ein anderer allgemeiner pythonischer Ansatz besteht darin, eine iterierbare Methode anzunehmen und dann ordnungsgemäß fehlzuschlagen, wenn sie für das angegebene Objekt nicht funktioniert. Das Python-Glossar:
Pythonic Programmierstil , die eine Objekttyp durch Inspektion seiner Methode oder Attribut Unterschrift und nicht durch explizite Beziehung zu einem gewissen Typ Objekt bestimmt ( „Wenn es aussieht wie ein Ente und quakt wie eine Ente , muss es sein , Ente .“) Durch die Betonung Schnittstellen Anstelle bestimmter Typen verbessert gut gestalteter Code seine Flexibilität, indem er eine polymorphe Substitution ermöglicht. Duck-Typing vermeidet Tests mit type () oder isinstance (). Stattdessen wird normalerweise der EAFP-Programmierstil (einfacher um Vergebung als um Erlaubnis zu bitten) verwendet.
...
try:
_ =(e for e in my_object)exceptTypeError:print my_object,'is not iterable'
Das collectionsModul bietet einige abstrakte Basisklassen, mit denen Klassen oder Instanzen gefragt werden können, ob sie bestimmte Funktionen bieten, z. B.:
from collections.abc importIterableif isinstance(e,Iterable):# e is iterable
Dies prüft jedoch nicht, ob Klassen iterierbar sind __getitem__.
[e for e in my_object]kann aus anderen Gründen eine Ausnahme auslösen, dh my_objectundefinierte oder mögliche Fehler bei der my_objectImplementierung.
Nick Dandoulakis
37
Ein String ist eine Folge ( isinstance('', Sequence) == True) und als beliebiger Reihenfolge es ist iterable ( isinstance('', Iterable)). Obwohl hasattr('', '__iter__') == Falseund es könnte verwirrend sein.
JFS
82
Wenn my_objectes sehr groß ist (z. B. unendlich itertools.count()), nimmt Ihr Listenverständnis viel Zeit / Speicher in Anspruch. Es ist besser, einen Generator zu erstellen, der niemals versucht, eine (möglicherweise unendliche) Liste zu erstellen.
Chris Lutz
14
Was ist, wenn some_object TypeError auslöst, das auch aus anderen Gründen (Bugs usw.) verursacht wurde? Wie können wir es vom "nicht iterierbaren TypeError" unterscheiden?
Shaung
54
Beachten Sie, dass in Python 3: hasattr(u"hello", '__iter__')zurückkehrtTrue
Carlos
572
Ente tippen
try:
iterator = iter(theElement)exceptTypeError:# not iterableelse:# iterable# for obj in iterator:# pass
Typprüfung
Verwenden Sie die abstrakten Basisklassen . Sie benötigen mindestens Python 2.6 und funktionieren nur für Klassen neuen Stils.
from collections.abc importIterable# import directly from collections for Python < 3.3if isinstance(theElement,Iterable):# iterableelse:# not iterable
Durch das Überprüfen werden isinstance(obj, Iterable)Klassen erkannt, die als iterierbar registriert sind oder eine __iter__()Methode haben, aber keine Klassen, die mit der __getitem__()
Methode iterieren . Der einzige zuverlässige Weg, um festzustellen, ob ein Objekt iterierbar ist, ist der Aufruf iter(obj).
Aus "Fluent Python" von Luciano Ramalho: Ab Python 3.4 können Sie am genauesten überprüfen, ob ein Objekt x iterierbar ist, indem Sie iter (x) aufrufen und eine TypeError-Ausnahme behandeln, wenn dies nicht der Fall ist. Dies ist genauer als die Verwendung von isinstance (x, abc.Iterable), da iter (x) auch die Legacy- Getitem- Methode berücksichtigt , während Iterable ABC dies nicht tut.
RdB
Wenn Sie denken "Oh, ich werde nur isinstance(x, (collections.Iterable, collections.Sequence))statt iter(x)", beachten Sie, dass dies immer noch kein iterierbares Objekt erkennt, das nur implementiert, __getitem__aber nicht __len__. Verwenden iter(x)und fangen Sie die Ausnahme.
Dale
Ihre zweite Antwort funktioniert nicht. In PyUNO, wenn ich das tue iter(slide1), geht alles gut, aber die isinstance(slide1, Iterable)Würfe TypeError: issubclass() arg 1 must be a class.
Hi-Angel
@ Hi-Angel klingt wie ein Fehler in PyUNOBeachten Sie, dass Ihre Fehlermeldung issubclass()statt sagt isinstance().
Georg Schölly
2
Das Aufrufen von iter () über ein Objekt kann eine teure Operation sein (siehe DataLoader in Pytorch, der mehrere Prozesse auf iter () abspaltet / erzeugt).
Szali
126
Ich möchte ein wenig mehr Licht biss sich auf das Zusammenspiel von iter, __iter__und , __getitem__und was hinter den Kulissen passiert. Mit diesem Wissen können Sie verstehen, warum das Beste, was Sie tun können, ist
try:
iter(maybe_iterable)print('iteration will probably work')exceptTypeError:print('not iterable')
Ich werde zuerst die Fakten auflisten und anschließend kurz daran erinnern, was passiert, wenn Sie eine forSchleife in Python verwenden, gefolgt von einer Diskussion, um die Fakten zu veranschaulichen.
Fakten
Sie können einen Iterator von jedem Objekt abrufen, oindem Sie aufrufen, iter(o)ob mindestens eine der folgenden Bedingungen erfüllt ist:
a) ohat eine __iter__Methode, die ein Iteratorobjekt zurückgibt. Ein Iterator ist ein beliebiges Objekt mit einer __iter__und einer __next__(Python 2 :) next-Methode.
b) ohat eine __getitem__Methode.
Es reicht nicht aus, nach einer Instanz von Iterableoder zu Sequencesuchen oder nach dem Attribut zu __iter__suchen.
Wenn ein Objekt onur implementiert __getitem__, aber nicht __iter__, iter(o)wird ein Iterator erstellt, der versucht, Elemente oanhand eines ganzzahligen Index ab Index 0 abzurufen . Der Iterator fängt alle IndexError(aber keine anderen Fehler) ab, die ausgelöst werden, und löst StopIterationsich dann selbst aus.
Im allgemeinsten Sinne gibt es keine iterandere Möglichkeit, zu überprüfen, ob der von zurückgegebene Iterator vernünftig ist, als ihn auszuprobieren.
Wenn ein Objekt oimplementiert wird __iter__, stellt die iterFunktion sicher, dass das von zurückgegebene Objekt __iter__ein Iterator ist. Es gibt keine Überprüfung der Integrität, wenn ein Objekt nur implementiert wird __getitem__.
__iter__Gewinnt. Wenn ein Objekt obeide __iter__und implementiert __getitem__, iter(o)wird aufgerufen __iter__.
Wenn Sie Ihre eigenen Objekte iterierbar machen möchten, implementieren Sie immer die __iter__Methode.
for Schleifen
Um mitzumachen, müssen Sie verstehen, was passiert, wenn Sie eine forSchleife in Python verwenden. Wenn Sie es bereits wissen, können Sie direkt zum nächsten Abschnitt springen.
Wenn Sie for item in ofür ein iterierbares Objekt verwenden o, ruft Python iter(o)ein Iteratorobjekt auf und erwartet es als Rückgabewert. Ein Iterator ist ein beliebiges Objekt, das eine __next__(oder nextin Python 2) Methode und eine __iter__Methode implementiert .
Konventionell sollte die __iter__Methode eines Iterators das Objekt selbst zurückgeben (dh return self). Python ruft dann nextden Iterator auf, bis er ausgelöst StopIterationwird. All dies geschieht implizit, aber die folgende Demonstration macht es sichtbar:
import random
classDemoIterable(object):def __iter__(self):print('__iter__ called')returnDemoIterator()classDemoIterator(object):def __iter__(self):return self
def __next__(self):print('__next__ called')
r = random.randint(1,10)if r ==5:print('raising StopIteration')raiseStopIterationreturn r
Iteration über a DemoIterable:
>>> di =DemoIterable()>>>for x in di:...print(x)...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
Diskussion und Illustrationen
Zu Punkt 1 und 2: Erhalten eines Iterators und unzuverlässiger Überprüfungen
Aus diesem Grund empfiehlt Fluent Python von Luciano Ramalho, iterdas Potenzial aufzurufen und zu behandeln TypeError, um zu überprüfen, ob ein Objekt iterierbar ist. Zitat direkt aus dem Buch:
Ab Python 3.4 können Sie am genauesten überprüfen, ob ein Objekt xiterierbar ist, indem Sie iter(x)eine TypeErrorAusnahme aufrufen und behandeln, wenn dies nicht der Fall ist. Dies ist genauer als die Verwendung isinstance(x, abc.Iterable), da iter(x)auch die Legacy- __getitem__Methode berücksichtigt wird, während dies beim IterableABC nicht der Fall ist.
Zu Punkt 3: Iterieren über Objekte, die nur liefern __getitem__, aber nicht__iter__
Durchlaufen einer Instanz von BasicIterableArbeiten wie erwartet: Python erstellt einen Iterator, der versucht, Elemente anhand des Index abzurufen, beginnend bei Null, bis ein ausgelöst IndexErrorwird. Die __getitem__Methode des Demo-Objekts gibt einfach das zurück, itemwas __getitem__(self, item)vom Iterator, der von zurückgegeben wurde, als Argument angegeben wurde iter.
>>> b =BasicIterable()>>> it = iter(b)>>> next(it)0>>> next(it)1>>> next(it)2>>> next(it)Traceback(most recent call last):File"<stdin>", line 1,in<module>StopIteration
Beachten Sie, dass der Iterator ausgelöst wird, StopIterationwenn er das nächste Element nicht zurückgeben kann, und dass das IndexError, für item == 3das ausgelöst wird, intern behandelt wird. Aus diesem Grund funktioniert das Schleifen über a BasicIterablemit einer forSchleife wie erwartet:
>>>for x in b:...print(x)...012
Hier ist ein weiteres Beispiel, um das Konzept zu verdeutlichen, wie der von zurückgegebene Iterator iterversucht, über den Index auf Elemente zuzugreifen. WrappedDicterbt nicht von dict, was bedeutet, dass Instanzen keine __iter__Methode haben.
classWrappedDict(object):# note: no inheritance from dict!def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):try:return self._dict[item]# delegate to dict.__getitem__exceptKeyError:raiseIndexError
Beachten Sie, dass Aufrufe an __getitem__delegiert werden, dict.__getitem__für die die eckige Klammer nur eine Abkürzung ist.
>>> w =WrappedDict({-1:'not printed',...0:'hi',1:'StackOverflow',2:'!',...4:'not printed',...'x':'not printed'})>>>for x in w:...print(x)...
hi
StackOverflow!
Zu Punkt 4 und 5: iterÜberprüft beim Aufrufen nach einem Iterator__iter__ :
Wenn iter(o)für ein Objekt aufgerufen wird o, iterwird sichergestellt, dass der Rückgabewert von __iter__, wenn die Methode vorhanden ist, ein Iterator ist. Dies bedeutet, dass das zurückgegebene Objekt __next__(oder nextin Python 2) und implementieren muss __iter__. iterEs können keine Integritätsprüfungen für Objekte durchgeführt werden, die nur bereitstellen __getitem__, da nicht überprüft werden kann, ob auf die Elemente des Objekts über einen Ganzzahlindex zugegriffen werden kann.
classFailIterIterable(object):def __iter__(self):return object()# not an iteratorclassFailGetitemIterable(object):def __getitem__(self, item):raiseException
Beachten Sie, dass das Erstellen eines Iterators aus FailIterIterableInstanzen sofort fehlschlägt, während das Erstellen eines Iterators aus Instanzen FailGetItemIterableerfolgreich ist, aber beim ersten Aufruf von eine Ausnahme auslöst __next__.
>>> fii =FailIterIterable()>>> iter(fii)Traceback(most recent call last):File"<stdin>", line 1,in<module>TypeError: iter() returned non-iterator of type 'object'>>>>>> fgi =FailGetitemIterable()>>> it = iter(fgi)>>> next(it)Traceback(most recent call last):File"<stdin>", line 1,in<module>File"/path/iterdemo.py", line 42,in __getitem__
raiseExceptionException
Zu Punkt 6: __iter__gewinnt
Dieser ist unkompliziert. Wenn ein Objekt implementiert __iter__und __getitem__, iterwird aufgerufen __iter__. Betrachten Sie die folgende Klasse
>>> iwd =IterWinsDemo()>>>for x in iwd:...print(x)...
__iter__
wins
Zu Punkt 7: Ihre iterierbaren Klassen sollten implementiert werden __iter__
Sie könnten sich fragen, warum die meisten eingebauten Sequenzen wie das listImplementieren einer __iter__Methode __getitem__ausreichen würden.
classWrappedList(object):# note: no inheritance from list!def __init__(self, lst):
self._list = lst
def __getitem__(self, item):return self._list[item]
Immerhin Iteration über Instanzen der Klasse oben, die Delegierten Anrufe __getitem__an list.__getitem__(die eckige Klammer - Notation), werden gut funktionieren:
>>> wl =WrappedList(['A','B','C'])>>>for x in wl:...print(x)...
A
B
C
Die Gründe, die Ihre benutzerdefinierten Iterables implementieren sollten, __iter__sind folgende:
Wenn Sie implementieren __iter__, werden Instanzen als iterabel betrachtet und isinstance(o, collections.abc.Iterable)zurückgegeben True.
Wenn das von zurückgegebene Objekt __iter__kein Iterator ist, iterschlägt dies sofort fehl und löst a aus TypeError.
Die spezielle Behandlung von __getitem__besteht aus Gründen der Abwärtskompatibilität. Nochmals aus Fluent Python zitieren:
Deshalb ist jede Python-Sequenz iterierbar: Sie alle implementieren __getitem__. Tatsächlich werden auch die Standardsequenzen implementiert __iter__, und Ihre sollten dies auch tun, da die spezielle Behandlung von __getitem__aus Gründen der Abwärtskompatibilität besteht und möglicherweise in Zukunft weg sein wird (obwohl sie beim Schreiben nicht veraltet ist).
Es ist also sicher, ein Prädikat zu definieren, is_iterableindem Sie Truein den tryBlock und Falsein den except TypeErrorBlock zurückkehren.
Alancalvitti
Dies ist eine großartige Antwort. Ich denke, es unterstreicht die unintuitive und unglückliche Natur des getitem-Protokolls. Es sollte niemals hinzugefügt worden sein.
Neil G
31
Dies ist nicht ausreichend: Das von zurückgegebene Objekt __iter__muss das Iterationsprotokoll (dh die nextMethode) implementieren . Siehe den entsprechenden Abschnitt in der Dokumentation .
In Python empfiehlt es sich, "zu versuchen und zu sehen" anstatt "zu überprüfen".
@willem: oder "bitte nicht um Erlaubnis, sondern um Vergebung" ;-)
jldupont
14
@willem Sowohl die Stile "Erlaubnis" als auch "Vergebung" gelten als Ententypisierung. Wenn Sie fragen, was ein Objekt tun kann , anstatt was es ist , dann ist das Entenschreiben. Wenn Sie Introspektion verwenden, ist das "Erlaubnis"; Wenn Sie nur versuchen, es zu tun und zu sehen, ob es funktioniert oder nicht, ist das "Vergebung".
Mark Reed
22
In Python <= 2.5 können und sollten Sie nicht - iterable war eine "informelle" Schnittstelle.
Seit Python 2.6 und 3.0 können Sie jedoch die neue ABC-Infrastruktur (Abstract Base Class) zusammen mit einigen integrierten ABCs nutzen, die im Sammlungsmodul verfügbar sind:
from collections importIterableclassMyObject(object):pass
mo =MyObject()print isinstance(mo,Iterable)Iterable.register(MyObject)print isinstance(mo,Iterable)print isinstance("abc",Iterable)
Ob dies wünschenswert ist oder tatsächlich funktioniert, ist nur eine Frage der Konventionen. Wie Sie sehen können, Sie können eine nicht iterable Objekt als Iterable registrieren - und es wird eine Ausnahme zur Laufzeit erhöhen. Daher erhält isinstance eine "neue" Bedeutung - es prüft lediglich die "deklarierte" Typkompatibilität, was in Python ein guter Weg ist.
Was werden Sie tun, wenn Ihr Objekt die von Ihnen benötigte Schnittstelle nicht erfüllt? Nehmen Sie das folgende Beispiel:
from collections importIterablefrom traceback import print_exc
def check_and_raise(x):ifnot isinstance(x,Iterable):raiseTypeError,"%s is not iterable"% x
else:for i in x:print i
def just_iter(x):for i in x:print i
classNotIterable(object):passif __name__ =="__main__":try:
check_and_raise(5)except:
print_exc()printtry:
just_iter(5)except:
print_exc()printtry:Iterable.register(NotIterable)
ni =NotIterable()
check_and_raise(ni)except:
print_exc()print
Wenn das Objekt nicht Ihren Erwartungen entspricht, werfen Sie einfach einen TypeError aus. Wenn jedoch das richtige ABC registriert wurde, ist Ihre Prüfung nicht sinnvoll. Im Gegenteil, wenn die __iter__Methode verfügbar ist, erkennt Python das Objekt dieser Klasse automatisch als iterierbar.
Wenn Sie also nur ein Iterable erwarten, iterieren Sie darüber und vergessen Sie es. Wenn Sie jedoch je nach Eingabetyp unterschiedliche Aufgaben ausführen müssen, ist die ABC-Infrastruktur möglicherweise sehr nützlich.
Verwenden Sie except:im Beispielcode kein Bare für Anfänger. Es fördert schlechte Praxis.
JFS
JFS: Das würde ich nicht, aber ich musste mehrere Codes zum Auslösen von Ausnahmen durchgehen und wollte die spezifische Ausnahme nicht abfangen ... Ich denke, der Zweck dieses Codes ist ziemlich klar.
Alan Franzoni
21
try:#treat object as iterableexceptTypeError, e:#object is not actually iterable
Führen Sie keine Überprüfungen durch, um festzustellen, ob Ihre Ente wirklich eine Ente ist, um festzustellen, ob sie iterierbar ist oder nicht. Behandeln Sie sie so, als ob sie es wäre, und beschweren Sie sich, wenn dies nicht der Fall ist.
Technisch gesehen könnte Ihre Berechnung während der Iteration ein werfen TypeErrorund Sie hier abwerfen, aber im Grunde ja.
Chris Lutz
6
@willem: Bitte verwenden Sie timeit, um einen Benchmark durchzuführen. Python-Ausnahmen sind oft schneller als if-Anweisungen. Sie können einen etwas kürzeren Weg durch den Dolmetscher nehmen.
S.Lott
2
@willem: IronPython hat langsame (im Vergleich zu CPython) Ausnahmen.
JFS
2
Ein funktionierender Versuch: Aussage ist wirklich schnell. Wenn Sie also nur wenige Ausnahmen haben, ist try-exception schnell. Wenn Sie viele Ausnahmen erwarten, kann das „Wenn“ schneller sein.
Arne Babenhauserheide
2
Sollte das Ausnahmeobjekt nicht durch Hinzufügen von " as e" nach TypeErrorstatt durch Hinzufügen von " , e" abgefangen werden ?
HelloGoodbye
21
Seit Python 3.5 können Sie das Typisierungsmodul aus der Standardbibliothek für typbezogene Dinge verwenden:
from typing importIterable...if isinstance(my_item,Iterable):print(True)
Dies prüft grundsätzlich, ob das Objekt den inOperator implementiert .
Vorteile (keine der anderen Lösungen hat alle drei):
es ist ein Ausdruck (funktioniert als Lambda , im Gegensatz zum Versuch ... außer Variante)
Es wird (sollte) von allen Iterables implementiert, einschließlich Strings (im Gegensatz zu __iter__).
funktioniert auf jedem Python> = 2.5
Anmerkungen:
Die Python-Philosophie "Bitte um Vergebung, nicht um Erlaubnis" funktioniert nicht gut, wenn z. B. in einer Liste sowohl iterable als auch nicht iterable Elemente vorhanden sind und Sie jedes Element je nach Typ unterschiedlich behandeln müssen (Behandlung von iterables bei try und non- iterables on außer würde funktionieren, aber es würde hintern hässlich und irreführend aussehen)
Lösungen für dieses Problem, bei denen versucht wird, das Objekt tatsächlich zu iterieren (z. B. [x für x in obj]), um zu überprüfen, ob es iterierbar ist, können zu erheblichen Leistungseinbußen bei großen iterablen Objekten führen (insbesondere, wenn Sie nur die ersten Elemente des iterierbaren Objekts benötigen, z Beispiel) und sollte vermieden werden
Schön, aber warum nicht das Sammlungsmodul verwenden, wie in stackoverflow.com/questions/1952464/… vorgeschlagen ? Scheint mir ausdrucksvoller zu sein.
Dave Abrahams
1
Es ist kürzer (und erfordert keine zusätzlichen Importe), ohne an Klarheit zu verlieren: Eine "enthält" -Methode ist eine natürliche Methode, um zu überprüfen, ob es sich bei etwas um eine Sammlung von Objekten handelt.
Vlad
46
Nur weil etwas etwas enthalten kann, heißt das nicht unbedingt, dass es iterierbar ist. Ein Benutzer kann beispielsweise überprüfen, ob sich ein Punkt in einem 3D-Würfel befindet. Wie würden Sie dieses Objekt jedoch durchlaufen?
Casey Kuball
13
Das ist falsch. Ein iterable selbst unterstützt keine enthält , zumindest mit Python 3.4.
Peter Shinners
15
Sie könnten dies versuchen:
def iterable(a):try:(x for x in a)returnTrueexceptTypeError:returnFalse
Wenn wir einen Generator erstellen können, der darüber iteriert (aber niemals den Generator verwendet, damit er keinen Platz beansprucht), ist er iterierbar. Scheint wie eine "duh" Sache. Warum müssen Sie feststellen, ob eine Variable überhaupt iterierbar ist?
@badp, das (x for x in a)erstellt nur einen Generator, es macht keine Iteration auf einem.
catchmeifyoutry
5
Ist das Ausprobieren (x for x in a)genau gleichbedeutend mit dem Ausprobieren iterator = iter(a)? Oder gibt es einige Fälle, in denen die beiden unterschiedlich sind?
Max
Ist das nicht for _ in a: breakeinfacher? Ist es langsamer?
Mr_and_Mrs_D
2
@Mr_and_Mrs_D Das ist schlecht, wenn das getestete Objekt ein Iterator ist, der anschließend wiederholt wird (es wird 1 Element fehlen, da seine Position nicht zurückgesetzt werden kann). Das Erstellen von Garbage Generatoren iteriert nicht über das Objekt, da sie nicht wiederholt werden. obwohl ich nicht sicher bin, ob es einen TypeError zu 100% auslöst, wenn es nicht iterierbar ist.
alle Sequenztypen (z list , str, und tuple) und einige nicht-Sequenztypen wie dictund fileund Objekte aller Klassen , die Sie mit ein definieren __iter__()oder __getitem__()Verfahren. Iterables können in einer for-Schleife und an vielen anderen Stellen verwendet werden, an denen eine Sequenz benötigt wird (zip (), map (), ...). Wenn ein iterierbares Objekt als Argument an die integrierte Funktion iter () übergeben wird, wird ein Iterator für das Objekt zurückgegeben.
Angesichts des allgemeinen Codierungsstils für Python, der auf der Tatsache basiert, dass es „einfacher ist, um Vergebung zu bitten als um Erlaubnis“, besteht die allgemeine Erwartung darin, diese zu verwenden
try:for i in object_in_question:
do_something
exceptTypeError:
do_something_for_non_iterable
Wenn Sie dies jedoch explizit überprüfen müssen, können Sie nach einem iterierbaren Test suchen hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). Sie müssen nach beiden suchen, da strs keine __iter__Methode haben (zumindest nicht in Python 2, in Python 3) und weil generatorObjekte keine __getitem__Methode haben.
Wann immer Sie etwas mögen if x: return Trueelse: return False(mit xBooleschen Werten), können Sie dies als schreiben return x. In deinem Fall return isinstance(…)ohne if.
Alfe
Da Sie anerkennen, dass die Lösung von Alfe besser ist, warum haben Sie Ihre Antwort nicht bearbeitet, um das einfach zu sagen? Stattdessen haben Sie jetzt BEIDE Versionen in Ihrer Antwort. Unnötige Ausführlichkeit. Senden einer Bearbeitung, um dies zu beheben.
ToolmakerSteve
2
Sie sollten "TypeError" in der Zeile "außer: return False" abfangen. Alles zu fangen ist ein schlechtes Muster.
Mariusz Jamro
Wisse das. Ich habe diesen Code aus der NumPy-Bibliothek übersetzt, die die generische Ausnahme verwendet.
Fmonegaglia
Nur weil ein Code aus NumPy stammt, heißt das nicht, dass er gut ist ... Muster oder nicht. Das einzige Mal, wenn alles abgefangen werden sollte, ist, wenn Sie explizit Fehler in Ihrem Programm behandeln.
Dies sieht jedoch nur so aus, ob es __iter__Sequenzen und ähnliches gibt und nicht.
Lesen Sie den
4
Es ist mir immer entgangen, warum Python hat, callable(obj) -> boolaber nicht iterable(obj) -> bool...
sicherlich ist es einfacher zu tunhasattr(obj,'__call__') selbst wenn es langsamer ist.
Da fast jede andere Antwort die Verwendung von try/ empfiehlt except TypeError, wobei das Testen auf Ausnahmen in einer Sprache im Allgemeinen als schlechte Praxis angesehen wird, ist hier eine Implementierung von, die iterable(obj) -> boolich immer häufiger mag und verwende:
Um Python 2 willen werde ich ein Lambda nur für diesen zusätzlichen Leistungsschub verwenden ...
(In Python 3 spielt es keine Rolle, was Sie zum Definieren der Funktion verwenden, es defhat ungefähr die gleiche Geschwindigkeit wie lambda)
Beachten Sie, dass diese Funktion für Objekte mit schneller ausgeführt wird, __iter__da sie nicht getestet wird__getitem__ .
Die meisten iterierbaren Objekte sollten sich darauf verlassen, __iter__wo Sonderfallobjekte zurückgreifen __getitem__, obwohl beides erforderlich ist, damit ein Objekt iterierbar ist.
(und da dies Standard ist, betrifft es auch C-Objekte)
Er stellt keinen Arbeitscode zur Verfügung, geschweige denn über die Python-Leistung ... obwohl diese Antwort wirklich nur der Einfachheit halber war, wie ich es hier schon oft gesehen habe.
Tcll
3
def is_iterable(x):try:0in x
exceptTypeError:returnFalseelse:returnTrue
Dies sagt Ja zu allen Arten von iterierbaren Objekten, aber es sagt Nein zu Zeichenfolgen in Python 2 . (Das ist es, was ich zum Beispiel möchte, wenn eine rekursive Funktion einen String oder einen Container mit Strings aufnehmen kann. In dieser Situation kann das Bitten um Vergebung zu Obfuscode führen, und es ist besser, zuerst um Erlaubnis zu bitten.)
Hinweis: is_iterable () sagt Ja zu Zeichenfolgen vom Typ bytesund bytearray.
bytesObjekte in Python 3 sind iterierbar True == is_iterable(b"string") == is_iterable("string".encode('utf-8'))In Python 2 gibt es keinen solchen Typ.
bytearray Objekte in Python 2 und 3 sind iterierbar True == is_iterable(bytearray(b"abc"))
Der OP- hasattr(x, '__iter__')Ansatz sagt Ja zu Zeichenfolgen in Python 3 und Nein zu Python 2 (egal ob ''oder b''oder u''). Vielen Dank an @LuisMasuelli, dass Sie bemerkt haben, dass Sie auch einen Buggy im Stich lassen werden __iter__.
Der einfachste Weg, unter Wahrung des Pythons Ente eingeben , ist der Fehler zu fangen (Python weiß genau , was tut es von einem Objekt erwartet einen Iterator zu werden):
class A(object):def __getitem__(self, item):return something
class B(object):def __iter__(self):# Return a compliant iterator. Just an examplereturn iter([])class C(object):def __iter__(self):# Return crapreturn1class D(object):passdef iterable(obj):try:
iter(obj)returnTrueexcept:returnFalseassert iterable(A())assert iterable(B())assert iterable(C())assertnot iterable(D())
Anmerkungen :
Es ist unerheblich, ob das Objekt nicht iterierbar ist oder ein Buggy __iter__implementiert wurde, wenn der Ausnahmetyp derselbe ist: Sie können das Objekt ohnehin nicht iterieren.
Ich glaube, ich verstehe Ihr Anliegen: Wie callablebesteht eine Prüfung, ob ich mich auch auf die Eingabe von Enten verlassen kann, um ein AttributeErrorWenn auszulösen, das __call__für mein Objekt nicht definiert ist, aber dies ist bei iterierbaren Prüfungen nicht der Fall?
Ich kenne die Antwort nicht, aber Sie können entweder die von mir (und anderen Benutzern) angegebene Funktion implementieren oder einfach die Ausnahme in Ihrem Code abfangen (Ihre Implementierung in diesem Teil entspricht der von mir geschriebenen Funktion - stellen Sie einfach sicher, dass Sie die isolieren Iterator-Erstellung aus dem Rest des Codes, damit Sie die Ausnahme erfassen und von einer anderen unterscheiden können TypeError.
fruits =("apple","banana","peach")
isiterable(fruits)# returns True
num =345
isiterable(num)# returns False
isiterable(str)# returns False because str type is type class and it's not iterable.
hello ="hello dude !"
isiterable(hello)# returns True because as you know string objects are iterable
so viele detaillierte Antworten oben mit vielen positiven Stimmen und Sie werfen eine ungeklärte Antwort ein ... meh
Nrzonline
Bitte geben Sie keinen bloßen Code ein. Fügen Sie auch eine Erklärung hinzu, was dies tut.
Jonathan Mee
1
Anstatt nach dem __iter__Attribut zu __len__suchen , können Sie auch nach dem Attribut suchen, das von jedem iterierbaren Python implementiert wird, einschließlich Zeichenfolgen.
Nicht iterierbare Objekte würden dies aus offensichtlichen Gründen nicht implementieren. Es werden jedoch weder benutzerdefinierte Iterables abgefangen, die es nicht implementieren, noch Generatorausdrücke, die iterdamit umgehen können. Dies kann jedoch in einer Zeile erfolgen, und das Hinzufügen einer einfachen orAusdrucksprüfung für Generatoren würde dieses Problem beheben. (Beachten Sie, dass das Schreiben type(my_generator_expression) == generatoreinen NameErrorauslösen würde . Lesen Sie stattdessen diese Antwort.)
Sie können GeneratorType aus folgenden Typen verwenden:
>>>import types
>>> types.GeneratorType<class'generator'>>>> gen =(i for i in range(10))>>> isinstance(gen, types.GeneratorType)True
--- akzeptierte Antwort von utdemir
(Dies macht es nützlich, um zu überprüfen, ob Sie lendas Objekt aufrufen können .)
Leider verwenden nicht alle iterierbaren Objekte __len__... In diesem Fall wird normalerweise die Entfernung zwischen zwei Objekten nicht ordnungsgemäß berechnet. wo obj.dist()könnte leicht ersetzt werden.
Tcll
Ja. Die meisten benutzerdefinierten Iterables implementieren Iter und Getitem, jedoch nicht len. Integrierte Typen tun dies jedoch, und wenn Sie überprüfen möchten, ob Sie die len-Funktion darauf aufrufen können, ist die Überprüfung auf len sicherer. Aber Sie haben Recht.
DarthCadeus
0
Nicht wirklich "korrekt" , kann aber als schnelle Überprüfung der gängigsten Typen wie Zeichenfolgen, Tupel, Floats usw. dienen.
Ein bisschen spät zur Party, aber ich habe mir diese Frage gestellt und gesehen, dass mir dann eine Antwort einfällt. Ich weiß nicht, ob jemand dies bereits gepostet hat. Aber im Wesentlichen habe ich festgestellt, dass alle iterierbaren Typen __getitem __ () in ihrem Diktat haben. Auf diese Weise können Sie überprüfen, ob ein Objekt iterierbar ist, ohne es zu versuchen. (Wortspiel beabsichtigt)
__getitem__
ist auch ausreichend, um ein Objekt iterierbar zu macheniter(myObj)
erfolgreichisinstance(myObj, dict)
, wenn SiemyObj
also in beiden Fällen erfolgreich sind , wenn Sie sich eine Sequenz vondict
s oder eine einzelne ansehendict
. Eine Subtilität, die wichtig ist, wenn Sie wissen möchten, was eine Sequenz ist und was nicht. (in Python 2)__getitem__
ist auch ausreichend, um ein Objekt iterierbar zu machen ... wenn es bei Null beginnt .Antworten:
Ich habe dieses Problem in letzter Zeit ziemlich viel untersucht. Aufgrund dessen komme ich zu dem Schluss, dass dies heutzutage der beste Ansatz ist:
Das oben Gesagte wurde bereits früher empfohlen, aber der allgemeine Konsens war, dass die Verwendung
iter()
besser wäre:Wir haben
iter()
unseren Code auch für diesen Zweck verwendet, aber in letzter Zeit ärgere ich mich immer mehr über Objekte, die nur__getitem__
als iterierbar angesehen wurden. Es gibt triftige Gründe für__getitem__
ein nicht iterierbares Objekt, und mit ihnen funktioniert der obige Code nicht gut. Als Beispiel aus dem wirklichen Leben können wir Faker verwenden . Der obige Code gibt an, dass es iterierbar ist, aber der Versuch, es zu iterieren, führt zu einemAttributeError
(getestet mit Faker 4.0.2):Wenn wir verwenden würden
insinstance()
, würden wir Faker-Instanzen (oder andere Objekte, die nur__getitem__
) nicht versehentlich als iterierbar betrachten:In früheren Antworten wurde darauf hingewiesen, dass die Verwendung
iter()
sicherer ist, da die alte Methode zur Implementierung der Iteration in Python darauf basiert__getitem__
und derisinstance()
Ansatz dies nicht erkennen würde. Dies mag bei alten Python-Versionen der Fall gewesen sein, aber basierend auf meinen ziemlich umfassenden Testsisinstance()
funktioniert es heutzutage großartig. Der einzige Fall, in demisinstance()
dies nicht funktioniertiter()
hat, war dieUserDict
Verwendung von Python 2. Wenn dies relevant ist, können Sie es verwendenisinstance(item, (Iterable, UserDict))
, um dies zu behandeln.quelle
typing.Dict
als iterierbar angeseheniter(Dict)
,list(Dict)
schlägt aber mit Fehler fehlTypeError: Parameters to generic types must be types. Got 0.
. Wie erwartet wirdisinstance(Dict, Iterable)
false zurückgegeben.iter
die Verwendung wurde ein Teil unseres Codes, der "Pre-Caching" verwendete, unnötig verlangsamt. Wenn der__iter__
Code langsam ist, wird dies auch deriter
Fall sein ... jederzeit, wenn Sie nur sehen möchten, ob etwas iterierbar ist.Das Suchen nach Funktionen für
__iter__
Sequenztypen würde jedoch beispielsweise bei Zeichenfolgen in Python 2 fehlschlagen . Ich würde auch gerne die richtige Antwort wissen, bis dahin gibt es hier eine Möglichkeit (die auch bei Strings funktionieren würde):Das
iter
eingebaute prüft die__iter__
Methode oder bei Strings die__getitem__
Methode.Ein anderer allgemeiner pythonischer Ansatz besteht darin, eine iterierbare Methode anzunehmen und dann ordnungsgemäß fehlzuschlagen, wenn sie für das angegebene Objekt nicht funktioniert. Das Python-Glossar:
Das
collections
Modul bietet einige abstrakte Basisklassen, mit denen Klassen oder Instanzen gefragt werden können, ob sie bestimmte Funktionen bieten, z. B.:Dies prüft jedoch nicht, ob Klassen iterierbar sind
__getitem__
.quelle
[e for e in my_object]
kann aus anderen Gründen eine Ausnahme auslösen, dhmy_object
undefinierte oder mögliche Fehler bei dermy_object
Implementierung.isinstance('', Sequence) == True
) und als beliebiger Reihenfolge es ist iterable (isinstance('', Iterable)
). Obwohlhasattr('', '__iter__') == False
und es könnte verwirrend sein.my_object
es sehr groß ist (z. B. unendlichitertools.count()
), nimmt Ihr Listenverständnis viel Zeit / Speicher in Anspruch. Es ist besser, einen Generator zu erstellen, der niemals versucht, eine (möglicherweise unendliche) Liste zu erstellen.hasattr(u"hello", '__iter__')
zurückkehrtTrue
Ente tippen
Typprüfung
Verwenden Sie die abstrakten Basisklassen . Sie benötigen mindestens Python 2.6 und funktionieren nur für Klassen neuen Stils.
Ist
iter()
jedoch etwas zuverlässiger als in der Dokumentation beschrieben :quelle
isinstance(x, (collections.Iterable, collections.Sequence))
stattiter(x)
", beachten Sie, dass dies immer noch kein iterierbares Objekt erkennt, das nur implementiert,__getitem__
aber nicht__len__
. Verwendeniter(x)
und fangen Sie die Ausnahme.iter(slide1)
, geht alles gut, aber dieisinstance(slide1, Iterable)
WürfeTypeError: issubclass() arg 1 must be a class
.PyUNO
Beachten Sie, dass Ihre Fehlermeldungissubclass()
statt sagtisinstance()
.Ich möchte ein wenig mehr Licht biss sich auf das Zusammenspiel von
iter
,__iter__
und ,__getitem__
und was hinter den Kulissen passiert. Mit diesem Wissen können Sie verstehen, warum das Beste, was Sie tun können, istIch werde zuerst die Fakten auflisten und anschließend kurz daran erinnern, was passiert, wenn Sie eine
for
Schleife in Python verwenden, gefolgt von einer Diskussion, um die Fakten zu veranschaulichen.Fakten
Sie können einen Iterator von jedem Objekt abrufen,
o
indem Sie aufrufen,iter(o)
ob mindestens eine der folgenden Bedingungen erfüllt ist:a)
o
hat eine__iter__
Methode, die ein Iteratorobjekt zurückgibt. Ein Iterator ist ein beliebiges Objekt mit einer__iter__
und einer__next__
(Python 2 :)next
-Methode.b)
o
hat eine__getitem__
Methode.Es reicht nicht aus, nach einer Instanz von
Iterable
oder zuSequence
suchen oder nach dem Attribut zu__iter__
suchen.Wenn ein Objekt
o
nur implementiert__getitem__
, aber nicht__iter__
,iter(o)
wird ein Iterator erstellt, der versucht, Elementeo
anhand eines ganzzahligen Index ab Index 0 abzurufen . Der Iterator fängt alleIndexError
(aber keine anderen Fehler) ab, die ausgelöst werden, und löstStopIteration
sich dann selbst aus.Im allgemeinsten Sinne gibt es keine
iter
andere Möglichkeit, zu überprüfen, ob der von zurückgegebene Iterator vernünftig ist, als ihn auszuprobieren.Wenn ein Objekt
o
implementiert wird__iter__
, stellt dieiter
Funktion sicher, dass das von zurückgegebene Objekt__iter__
ein Iterator ist. Es gibt keine Überprüfung der Integrität, wenn ein Objekt nur implementiert wird__getitem__
.__iter__
Gewinnt. Wenn ein Objekto
beide__iter__
und implementiert__getitem__
,iter(o)
wird aufgerufen__iter__
.Wenn Sie Ihre eigenen Objekte iterierbar machen möchten, implementieren Sie immer die
__iter__
Methode.for
SchleifenUm mitzumachen, müssen Sie verstehen, was passiert, wenn Sie eine
for
Schleife in Python verwenden. Wenn Sie es bereits wissen, können Sie direkt zum nächsten Abschnitt springen.Wenn Sie
for item in o
für ein iterierbares Objekt verwendeno
, ruft Pythoniter(o)
ein Iteratorobjekt auf und erwartet es als Rückgabewert. Ein Iterator ist ein beliebiges Objekt, das eine__next__
(odernext
in Python 2) Methode und eine__iter__
Methode implementiert .Konventionell sollte die
__iter__
Methode eines Iterators das Objekt selbst zurückgeben (dhreturn self
). Python ruft dannnext
den Iterator auf, bis er ausgelöstStopIteration
wird. All dies geschieht implizit, aber die folgende Demonstration macht es sichtbar:Iteration über a
DemoIterable
:Diskussion und Illustrationen
Zu Punkt 1 und 2: Erhalten eines Iterators und unzuverlässiger Überprüfungen
Betrachten Sie die folgende Klasse:
Das Aufrufen
iter
mit einer Instanz vonBasicIterable
gibt einen Iterator ohne Probleme zurück, daBasicIterable
implementiert__getitem__
.Es ist jedoch wichtig zu beachten, dass
b
das__iter__
Attribut nicht vorhanden ist und nicht als Instanz vonIterable
oder betrachtet wirdSequence
:Aus diesem Grund empfiehlt Fluent Python von Luciano Ramalho,
iter
das Potenzial aufzurufen und zu behandelnTypeError
, um zu überprüfen, ob ein Objekt iterierbar ist. Zitat direkt aus dem Buch:Zu Punkt 3: Iterieren über Objekte, die nur liefern
__getitem__
, aber nicht__iter__
Durchlaufen einer Instanz von
BasicIterable
Arbeiten wie erwartet: Python erstellt einen Iterator, der versucht, Elemente anhand des Index abzurufen, beginnend bei Null, bis ein ausgelöstIndexError
wird. Die__getitem__
Methode des Demo-Objekts gibt einfach das zurück,item
was__getitem__(self, item)
vom Iterator, der von zurückgegeben wurde, als Argument angegeben wurdeiter
.Beachten Sie, dass der Iterator ausgelöst wird,
StopIteration
wenn er das nächste Element nicht zurückgeben kann, und dass dasIndexError
, füritem == 3
das ausgelöst wird, intern behandelt wird. Aus diesem Grund funktioniert das Schleifen über aBasicIterable
mit einerfor
Schleife wie erwartet:Hier ist ein weiteres Beispiel, um das Konzept zu verdeutlichen, wie der von zurückgegebene Iterator
iter
versucht, über den Index auf Elemente zuzugreifen.WrappedDict
erbt nicht vondict
, was bedeutet, dass Instanzen keine__iter__
Methode haben.Beachten Sie, dass Aufrufe an
__getitem__
delegiert werden,dict.__getitem__
für die die eckige Klammer nur eine Abkürzung ist.Zu Punkt 4 und 5:
iter
Überprüft beim Aufrufen nach einem Iterator__iter__
:Wenn
iter(o)
für ein Objekt aufgerufen wirdo
,iter
wird sichergestellt, dass der Rückgabewert von__iter__
, wenn die Methode vorhanden ist, ein Iterator ist. Dies bedeutet, dass das zurückgegebene Objekt__next__
(odernext
in Python 2) und implementieren muss__iter__
.iter
Es können keine Integritätsprüfungen für Objekte durchgeführt werden, die nur bereitstellen__getitem__
, da nicht überprüft werden kann, ob auf die Elemente des Objekts über einen Ganzzahlindex zugegriffen werden kann.Beachten Sie, dass das Erstellen eines Iterators aus
FailIterIterable
Instanzen sofort fehlschlägt, während das Erstellen eines Iterators aus InstanzenFailGetItemIterable
erfolgreich ist, aber beim ersten Aufruf von eine Ausnahme auslöst__next__
.Zu Punkt 6:
__iter__
gewinntDieser ist unkompliziert. Wenn ein Objekt implementiert
__iter__
und__getitem__
,iter
wird aufgerufen__iter__
. Betrachten Sie die folgende Klasseund die Ausgabe beim Durchlaufen einer Instanz:
Zu Punkt 7: Ihre iterierbaren Klassen sollten implementiert werden
__iter__
Sie könnten sich fragen, warum die meisten eingebauten Sequenzen wie das
list
Implementieren einer__iter__
Methode__getitem__
ausreichen würden.Immerhin Iteration über Instanzen der Klasse oben, die Delegierten Anrufe
__getitem__
anlist.__getitem__
(die eckige Klammer - Notation), werden gut funktionieren:Die Gründe, die Ihre benutzerdefinierten Iterables implementieren sollten,
__iter__
sind folgende:__iter__
, werden Instanzen als iterabel betrachtet undisinstance(o, collections.abc.Iterable)
zurückgegebenTrue
.__iter__
kein Iterator ist,iter
schlägt dies sofort fehl und löst a ausTypeError
.__getitem__
besteht aus Gründen der Abwärtskompatibilität. Nochmals aus Fluent Python zitieren:quelle
is_iterable
indem SieTrue
in dentry
Block undFalse
in denexcept TypeError
Block zurückkehren.Dies ist nicht ausreichend: Das von zurückgegebene Objekt
__iter__
muss das Iterationsprotokoll (dh dienext
Methode) implementieren . Siehe den entsprechenden Abschnitt in der Dokumentation .In Python empfiehlt es sich, "zu versuchen und zu sehen" anstatt "zu überprüfen".
quelle
In Python <= 2.5 können und sollten Sie nicht - iterable war eine "informelle" Schnittstelle.
Seit Python 2.6 und 3.0 können Sie jedoch die neue ABC-Infrastruktur (Abstract Base Class) zusammen mit einigen integrierten ABCs nutzen, die im Sammlungsmodul verfügbar sind:
Ob dies wünschenswert ist oder tatsächlich funktioniert, ist nur eine Frage der Konventionen. Wie Sie sehen können, Sie können eine nicht iterable Objekt als Iterable registrieren - und es wird eine Ausnahme zur Laufzeit erhöhen. Daher erhält isinstance eine "neue" Bedeutung - es prüft lediglich die "deklarierte" Typkompatibilität, was in Python ein guter Weg ist.
Was werden Sie tun, wenn Ihr Objekt die von Ihnen benötigte Schnittstelle nicht erfüllt? Nehmen Sie das folgende Beispiel:
Wenn das Objekt nicht Ihren Erwartungen entspricht, werfen Sie einfach einen TypeError aus. Wenn jedoch das richtige ABC registriert wurde, ist Ihre Prüfung nicht sinnvoll. Im Gegenteil, wenn die
__iter__
Methode verfügbar ist, erkennt Python das Objekt dieser Klasse automatisch als iterierbar.Wenn Sie also nur ein Iterable erwarten, iterieren Sie darüber und vergessen Sie es. Wenn Sie jedoch je nach Eingabetyp unterschiedliche Aufgaben ausführen müssen, ist die ABC-Infrastruktur möglicherweise sehr nützlich.
quelle
except:
im Beispielcode kein Bare für Anfänger. Es fördert schlechte Praxis.Führen Sie keine Überprüfungen durch, um festzustellen,
ob Ihre Ente wirklich eine Enteist, um festzustellen, ob sie iterierbar ist oder nicht. Behandeln Sie sie so, als ob sie es wäre, und beschweren Sie sich, wenn dies nicht der Fall ist.quelle
TypeError
und Sie hier abwerfen, aber im Grunde ja.as e
" nachTypeError
statt durch Hinzufügen von ", e
" abgefangen werden ?Seit Python 3.5 können Sie das Typisierungsmodul aus der Standardbibliothek für typbezogene Dinge verwenden:
quelle
Die beste Lösung, die ich bisher gefunden habe:
hasattr(obj, '__contains__')
Dies prüft grundsätzlich, ob das Objekt den
in
Operator implementiert .Vorteile (keine der anderen Lösungen hat alle drei):
__iter__
).Anmerkungen:
quelle
Sie könnten dies versuchen:
Wenn wir einen Generator erstellen können, der darüber iteriert (aber niemals den Generator verwendet, damit er keinen Platz beansprucht), ist er iterierbar. Scheint wie eine "duh" Sache. Warum müssen Sie feststellen, ob eine Variable überhaupt iterierbar ist?
quelle
iterable(itertools.repeat(0))
? :)(x for x in a)
erstellt nur einen Generator, es macht keine Iteration auf einem.(x for x in a)
genau gleichbedeutend mit dem Ausprobiereniterator = iter(a)
? Oder gibt es einige Fälle, in denen die beiden unterschiedlich sind?for _ in a: break
einfacher? Ist es langsamer?Ich habe hier eine schöne Lösung gefunden :
quelle
Nach dem Python 2-Glossar sind iterable
Angesichts des allgemeinen Codierungsstils für Python, der auf der Tatsache basiert, dass es „einfacher ist, um Vergebung zu bitten als um Erlaubnis“, besteht die allgemeine Erwartung darin, diese zu verwenden
Wenn Sie dies jedoch explizit überprüfen müssen, können Sie nach einem iterierbaren Test suchen
hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")
. Sie müssen nach beiden suchen, dastr
s keine__iter__
Methode haben (zumindest nicht in Python 2, in Python 3) und weilgenerator
Objekte keine__getitem__
Methode haben.quelle
In meinen Skripten finde ich es oft bequem, eine
iterable
Funktion zu definieren . (Enthält jetzt die von Alfe vorgeschlagene Vereinfachung):So können Sie testen, ob ein Objekt in der gut lesbaren Form iterierbar ist
wie Sie es mit der
callable
Funktion tun würdenBEARBEITEN: Wenn Sie numpy installiert haben, können Sie einfach tun: von
numpy import iterable
, was einfach so ähnlich istWenn Sie kein Numpy haben, können Sie einfach diesen oder den obigen Code implementieren.
quelle
if x: return True
else: return False
(mitx
Booleschen Werten), können Sie dies als schreibenreturn x
. In deinem Fallreturn isinstance(…)
ohneif
.Pandas hat eine eingebaute Funktion wie diese:
quelle
__iter__
Sequenzen und ähnliches gibt und nicht.Es ist mir immer entgangen, warum Python hat,
callable(obj) -> bool
aber nichtiterable(obj) -> bool
...sicherlich ist es einfacher zu tun
hasattr(obj,'__call__')
selbst wenn es langsamer ist.Da fast jede andere Antwort die Verwendung von
try
/ empfiehltexcept TypeError
, wobei das Testen auf Ausnahmen in einer Sprache im Allgemeinen als schlechte Praxis angesehen wird, ist hier eine Implementierung von, dieiterable(obj) -> bool
ich immer häufiger mag und verwende:Um Python 2 willen werde ich ein Lambda nur für diesen zusätzlichen Leistungsschub verwenden ...
(In Python 3 spielt es keine Rolle, was Sie zum Definieren der Funktion verwenden, es
def
hat ungefähr die gleiche Geschwindigkeit wielambda
)Beachten Sie, dass diese Funktion für Objekte mit schneller ausgeführt wird,
__iter__
da sie nicht getestet wird__getitem__
.Die meisten iterierbaren Objekte sollten sich darauf verlassen,
__iter__
wo Sonderfallobjekte zurückgreifen__getitem__
, obwohl beides erforderlich ist, damit ein Objekt iterierbar ist.(und da dies Standard ist, betrifft es auch C-Objekte)
quelle
Dies sagt Ja zu allen Arten von iterierbaren Objekten, aber es sagt Nein zu Zeichenfolgen in Python 2 . (Das ist es, was ich zum Beispiel möchte, wenn eine rekursive Funktion einen String oder einen Container mit Strings aufnehmen kann. In dieser Situation kann das Bitten um Vergebung zu Obfuscode führen, und es ist besser, zuerst um Erlaubnis zu bitten.)
Viele andere Strategien hier sagen Ja zu Strings. Verwenden Sie sie, wenn Sie dies möchten.
Hinweis: is_iterable () sagt Ja zu Zeichenfolgen vom Typ
bytes
undbytearray
.bytes
Objekte in Python 3 sind iterierbarTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
In Python 2 gibt es keinen solchen Typ.bytearray
Objekte in Python 2 und 3 sind iterierbarTrue == is_iterable(bytearray(b"abc"))
Der OP-
hasattr(x, '__iter__')
Ansatz sagt Ja zu Zeichenfolgen in Python 3 und Nein zu Python 2 (egal ob''
oderb''
oderu''
). Vielen Dank an @LuisMasuelli, dass Sie bemerkt haben, dass Sie auch einen Buggy im Stich lassen werden__iter__
.quelle
Der einfachste Weg, unter Wahrung des Pythons Ente eingeben , ist der Fehler zu fangen (Python weiß genau , was tut es von einem Objekt erwartet einen Iterator zu werden):
Anmerkungen :
__iter__
implementiert wurde, wenn der Ausnahmetyp derselbe ist: Sie können das Objekt ohnehin nicht iterieren.Ich glaube, ich verstehe Ihr Anliegen: Wie
callable
besteht eine Prüfung, ob ich mich auch auf die Eingabe von Enten verlassen kann, um einAttributeError
Wenn auszulösen, das__call__
für mein Objekt nicht definiert ist, aber dies ist bei iterierbaren Prüfungen nicht der Fall?Ich kenne die Antwort nicht, aber Sie können entweder die von mir (und anderen Benutzern) angegebene Funktion implementieren oder einfach die Ausnahme in Ihrem Code abfangen (Ihre Implementierung in diesem Teil entspricht der von mir geschriebenen Funktion - stellen Sie einfach sicher, dass Sie die isolieren Iterator-Erstellung aus dem Rest des Codes, damit Sie die Ausnahme erfassen und von einer anderen unterscheiden können
TypeError
.quelle
Die Funktion
isiterable
im folgenden Code wird zurückgegeben,True
wenn das Objekt iterierbar ist. Wenn es nicht iterierbar ist, wird zurückgegebenFalse
Beispiel
quelle
Anstatt nach dem
__iter__
Attribut zu__len__
suchen , können Sie auch nach dem Attribut suchen, das von jedem iterierbaren Python implementiert wird, einschließlich Zeichenfolgen.Nicht iterierbare Objekte würden dies aus offensichtlichen Gründen nicht implementieren. Es werden jedoch weder benutzerdefinierte Iterables abgefangen, die es nicht implementieren, noch Generatorausdrücke, die
iter
damit umgehen können. Dies kann jedoch in einer Zeile erfolgen, und das Hinzufügen einer einfachenor
Ausdrucksprüfung für Generatoren würde dieses Problem beheben. (Beachten Sie, dass das Schreibentype(my_generator_expression) == generator
einenNameError
auslösen würde . Lesen Sie stattdessen diese Antwort.)(Dies macht es nützlich, um zu überprüfen, ob Sie
len
das Objekt aufrufen können .)quelle
__len__
... In diesem Fall wird normalerweise die Entfernung zwischen zwei Objekten nicht ordnungsgemäß berechnet. woobj.dist()
könnte leicht ersetzt werden.Nicht wirklich "korrekt" , kann aber als schnelle Überprüfung der gängigsten Typen wie Zeichenfolgen, Tupel, Floats usw. dienen.
quelle
Ein bisschen spät zur Party, aber ich habe mir diese Frage gestellt und gesehen, dass mir dann eine Antwort einfällt. Ich weiß nicht, ob jemand dies bereits gepostet hat. Aber im Wesentlichen habe ich festgestellt, dass alle iterierbaren Typen __getitem __ () in ihrem Diktat haben. Auf diese Weise können Sie überprüfen, ob ein Objekt iterierbar ist, ohne es zu versuchen. (Wortspiel beabsichtigt)
quelle