1300 Stimmen

In Python, wie erkenne ich, ob ein Objekt iterierbar ist?

Gibt es eine Methode wie isiterable? Die einzige Lösung, die ich bisher gefunden habe, ist den folgenden Aufruf:

hasattr(myObj, '__iter__')

Aber ich bin mir nicht sicher, wie zuverlässig das ist.

953voto

miku Punkte 170688
  1. Das Überprüfen von __iter__ funktioniert bei Sequenztypen, würde aber z. B. bei Zeichenketten in Python 2 fehlschlagen. Ich würde auch gerne die richtige Antwort wissen, bis dahin hier ist eine Möglichkeit (die auch bei Zeichenketten funktionieren würde):

     try:
         some_object_iterator = iter(some_object)
     except TypeError as te:
         print(some_object, 'ist nicht iterierbar')

Die eingebaute Funktion iter überprüft die Methode __iter__ oder im Falle von Zeichenketten die Methode __getitem__.

  1. Ein weiterer allgemeiner Ansatz in Python ist, anzunehmen, dass es sich um ein iterierbares Objekt handelt, und dann genanntermaßen fehlerhaft zu handeln, wenn es nicht funktioniert. Die Python-Glossar:

Pythonischer Programmierstil, der den Typ eines Objekts durch Inspektion seiner Methode oder Attributsignatur bestimmt statt durch eine explizite Beziehung zu einem bestimmten Typobjekt ("Wenn es wie eine Ente aussieht und wie eine Ente quakt, dann muss es eine Ente sein."). Durch Betonung von Schnittstellen anstelle von spezifischen Typen verbessert gut gestalteter Code seine Flexibilität, indem er polymorphe Substitution ermöglicht. Duck-Typing vermeidet Tests mit type() oder isinstance(). Stattdessen verwendet es typischerweise den EAFP (Easier to Ask Forgiveness than Permission) Programmierstil.

...

try:
   _ = (e for e in my_object)
except TypeError:
   print my_object, 'ist nicht iterierbar'
  1. Das collections Modul bietet einige abstrakte Basisklassen, mit denen man Klassen oder Instanzen fragen kann, ob sie eine bestimmte Funktionalität bereitstellen, zum Beispiel:

     from collections.abc import Iterable
    
     if isinstance(e, Iterable):
         # e ist iterierbar

Allerdings wird hierbei nicht überprüft, ob Klassen iterierbar sind durch __getitem__.

657voto

Georg Schölly Punkte 120083

Duck typing

try:
    iterator = iter(the_element)
except TypeError:
    # nicht iterierbar
else:
    # iterierbar

# für obj in iterator:
#     pass

Type checking

Verwenden Sie die Abstract Base Classes. Sie benötigen mindestens Python 2.6 und funktionieren nur für neuartige Klassen.

from collections.abc import Iterable   # direkt aus collections importieren für Python < 3.3

if isinstance(the_element, Iterable):
    # iterierbar
else:
    # nicht iterierbar

Allerdings ist iter() wie in der Dokumentation beschrieben etwas zuverlässiger:

Bei der Überprüfung von isinstance(obj, Iterable) werden Klassen erkannt, die als Iterable registriert sind oder über eine __iter__()-Methode verfügen, jedoch nicht Klassen, die mit der __getitem__()-Methode iterieren. Der einzige zuverlässige Weg zu bestimmen, ob ein Objekt iterierbar ist, besteht darin, iter(obj) aufzurufen.

95voto

Pekka Klärck Punkte 2238

Ich habe dieses Problem in letzter Zeit ziemlich intensiv studiert. Basierend darauf ist mein Fazit, dass dies heutzutage der beste Ansatz ist:

from collections.abc import Iterable   # mit Python 2.7 oder niedriger '.abc' weglassen

def iterable(obj):
    return isinstance(obj, Iterable)

Das oben Genannte wurde bereits früher empfohlen, aber der allgemeine Konsens war, dass es besser wäre, iter() zu verwenden:

def iterable(obj):
    try:
        iter(obj)
    except Exception:
        return False
    else:
        return True

Wir haben in unserem Code auch iter() zu diesem Zweck verwendet, aber in letzter Zeit habe ich mich mehr und mehr über Objekte geärgert, die nur __getitem__ haben und als iterierbar betrachtet werden. Es gibt gültige Gründe, __getitem__ in einem nicht-iterierbaren Objekt zu haben, und mit ihnen funktioniert der obige Code nicht gut. Als ein echtes Beispiel können wir Faker verwenden. Der obige Code meldet, dass es iterierbar ist, aber beim Versuch, es zu durchlaufen, tritt ein AttributeError auf (getestet mit Faker 4.0.2):

>>> from faker import Faker
>>> fake = Faker()
>>> iter(fake)    # Keine Ausnahme, muss iterierbar sein

>>> list(fake)    # Ups
Traceback (most recent call last):
  File "", line 1, in 
  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 isinstance() verwenden würden, würden wir versehentlich Faker-Instanzen (oder andere Objekte, die nur __getitem__ haben) nicht als iterierbar betrachten:

>>> from collections.abc import Iterable
>>> from faker import Faker
>>> isinstance(Faker(), Iterable)
False

Frühere Antworten haben kommentiert, dass die Verwendung von iter() sicherer ist, da die alte Methode zur Implementierung der Iteration in Python auf __getitem__ basierte und der Ansatz mit isinstance() das nicht erkennen würde. Dies mag bei älteren Python-Versionen zutreffend gewesen sein, aber basierend auf meinen ziemlich umfangreichen Tests funktioniert isinstance() heutzutage großartig. Der einzige Fall, in dem isinstance() nicht funktionierte, aber iter() funktionierte, war mit UserDict bei Verwendung von Python 2. Wenn das relevant ist, ist es möglich, isinstance(item, (Iterable, UserDict)) zu verwenden, um dies abzudecken.

39voto

Rotareti Punkte 39876

Seit Python 3.5 können Sie das Typisierungs-Modul aus der Standardbibliothek für typbezogene Dinge verwenden:

from typing import Iterable

...

if isinstance(my_item, Iterable):
    print(True)

33voto

jldupont Punkte 87330

Dies reicht nicht aus: Das Objekt, das von __iter__ zurückgegeben wird, muss das Iterationsprotokoll implementieren (d.h. die next-Methode). Siehe den relevanten Abschnitt in der Dokumentation.

In Python ist es eine gute Praxis, "zu versuchen und zu sehen" anstatt "zu überprüfen".

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X