web-dev-qa-db-fra.com

En Python, comment déterminer si un objet est itérable?

Existe-t-il une méthode comme isiterable? La seule solution que j'ai trouvée jusqu'à présent est d'appeler

hasattr(myObj, '__iter__')

Mais je ne sais pas à quel point c'est infaillible.

884
willem
  1. La vérification de __iter__ fonctionne avec les types de séquence, mais elle échoue, par exemple. chaînes en Python 2. Je voudrais aussi connaître la bonne réponse. Jusque-là, voici une possibilité (qui fonctionnerait aussi avec les chaînes):

    try:
        some_object_iterator = iter(some_object)
    except TypeError as te:
        print some_object, 'is not iterable'
    

    La variable iter intégrée vérifie la méthode __iter__ ou, dans le cas de chaînes, la méthode __getitem__.

  2. Une autre approche générale de Pythonic consiste à supposer une valeur itérable, puis à échouer gracieusement si elle ne fonctionne pas sur l'objet donné. Le glossaire Python:

    Style de programmation pythonique qui détermine le type d’un objet en examinant sa signature de méthode ou d’attribut plutôt qu’en établissant une relation explicite avec un objet type ("Si cela ressemble à un duck et que des charlatans ressemblent à un duck, ce doit être a canard. ") En mettant l’accent sur les interfaces plutôt que sur des types spécifiques, un code bien conçu améliore sa flexibilité en permettant une substitution polymorphe. Duck-typing évite les tests utilisant type () ou isinstance (). Au lieu de cela, il utilise généralement le style de programmation EAFP (Plus facile à demander pardon que l’autorisation).

    ...

    try:
       _ = (e for e in my_object)
    except TypeError:
       print my_object, 'is not iterable'
    
  3. Le module collections fournit des classes de base abstraites, qui permettent de demander aux classes ou aux instances si elles fournissent des fonctionnalités particulières, par exemple:

    from collections.abc import Iterable
    
    if isinstance(e, Iterable):
        # e is iterable
    

    Cependant, cela ne vérifie pas les classes qui sont éditables via __getitem__.

712
miku

Canard en tapant

try:
    iterator = iter(theElement)
except TypeError:
    # not iterable
else:
    # iterable

# for obj in iterator:
#     pass

Vérification de type

Utilisez les classes de base Abstract . Ils ont besoin d’au moins Python 2.6 et ne fonctionnent que pour les classes de style nouveau.

from collections.abc import Iterable   # import directly from collections for Python < 3.3

if isinstance(theElement, Iterable):
    # iterable
else:
    # not iterable

Cependant, iter() est un peu plus fiable que décrit par la documentation :

La vérification de isinstance(obj, Iterable) détecte les classes qui sont enregistré comme Iterable ou ayant une méthode __iter__(), mais il ne détecte pas les classes qui itèrent avec la __getitem__() méthode. Le seul moyen fiable de déterminer s'il s'agit d'un objet est iterable est d'appeler iter(obj).

519
Georg Schölly

Cela ne suffit pas: l’objet retourné par __iter__ doit implémenter le protocole d’itération (méthode next). Voir la section correspondante dans documentation .

En Python, une bonne pratique consiste à "essayer de voir" au lieu de "vérifier".

30
jldupont
try:
  #treat object as iterable
except TypeError, e:
  #object is not actually iterable

Ne faites pas de vérifications pour voir si votre canard est vraiment un canard pour voir si c'est itérable ou non, traitez-le comme si c'était le cas et plaignez-vous s'il ne l'était pas.

20
badp

En Python <= 2.5, vous ne pouvez pas et ne devriez pas - iterable était une interface "informelle".

Mais depuis Python 2.6 et 3.0, vous pouvez tirer parti de la nouvelle infrastructure ABC (classe de base abstraite) ainsi que de certains ABC intégrés, disponibles dans le module Collections:

from collections import Iterable

class MyObject(object):
    pass

mo = MyObject()
print isinstance(mo, Iterable)
Iterable.register(MyObject)
print isinstance(mo, Iterable)

print isinstance("abc", Iterable)

Maintenant, si cela est souhaitable ou fonctionne réellement, est juste une question de conventions. Comme vous pouvez le constater, vous pouvez enregistrer un objet non-itérable comme étant Iterable - et une exception sera générée lors de l'exécution. Par conséquent, isinstance acquiert un nouveau sens: il vérifie simplement la compatibilité de type "déclaré", ce qui est une bonne façon de procéder en Python.

D'autre part, si votre objet ne satisfait pas l'interface dont vous avez besoin, qu'allez-vous faire? Prenons l'exemple suivant:

from collections import Iterable
from traceback import print_exc

def check_and_raise(x):
    if not isinstance(x, Iterable):
        raise TypeError, "%s is not iterable" % x
    else:
        for i in x:
            print i

def just_iter(x):
    for i in x:
        print i


class NotIterable(object):
    pass

if __== "__main__":
    try:
        check_and_raise(5)
    except:
        print_exc()
        print

    try:
        just_iter(5)
    except:
        print_exc()
        print

    try:
        Iterable.register(NotIterable)
        ni = NotIterable()
        check_and_raise(ni)
    except:
        print_exc()
        print

Si l'objet ne répond pas à vos attentes, vous lancez simplement un TypeError, mais si le bon ABC a été enregistré, votre vérification est inutile. Au contraire, si la méthode __iter__ est disponible, Python reconnaîtra automatiquement l’objet de cette classe comme étant Iterable.

Donc, si vous vous attendez à ce que vous soyez itératif, parcourez-le et oubliez-le. D'autre part, si vous devez faire différentes choses en fonction du type d'entrée, l'infrastructure ABC peut être très utile.

18
Alan Franzoni

La meilleure solution que j'ai trouvée jusqu'à présent:

hasattr(obj, '__contains__')

qui vérifie fondamentalement si l’objet implémente l’opérateur in.

Avantages (aucune des autres solutions n'a les trois):

  • c'est une expression (fonctionne comme un lambda, par opposition à la variante try ... except)
  • il est (devrait être) implémenté par tous les itérables, y compris chaînes (par opposition à __iter__)
  • fonctionne sur n'importe quel Python> = 2.5

Remarques:

  • la philosophie Python de "demander pardon, pas autorisation" ne fonctionne pas bien lorsque, par exemple. dans une liste, vous avez à la fois des iterables et des non-iterables et vous devez traiter chaque élément différemment en fonction de son type (traiter iterables sur try et non-iterables sur sauf que serait travail, mais il aurait l'air fainéant et trompeur)
  • les solutions à ce problème qui tentent d’itérer réellement une itération sur l’objet (par exemple [x pour x dans obj]) pour vérifier si c’est itérable peuvent induire des pénalités de performance significatives pour les grandes itérables (surtout si vous avez juste besoin des premiers éléments de la variable exemple) et doit être évité
18
Vlad

J'ai trouvé une belle solution ici :

isiterable = lambda obj: isinstance(obj, basestring) \
    or getattr(obj, '__iter__', False)
13
jbochi

Vous pouvez essayer ceci:

def iterable(a):
    try:
        (x for x in a)
        return True
    except TypeError:
        return False

Si nous pouvons créer un générateur qui itère dessus (mais ne l'utilisez jamais afin qu'il ne prenne pas de place), c'est itérable. On dirait un genre "duh". Pourquoi devez-vous déterminer si une variable est éditable en premier lieu?

12
Chris Lutz

Selon le Glossaire Python 2 , les iterables sont

tous les types de séquence (tels que list, str et Tuple) et certains types non séquentiels tels que dict et file et les objets de toutes les classes que vous définissez avec une méthode __iter__() ou __getitem__(). Iterables peut être utilisé dans une boucle for et dans de nombreux autres endroits où une séquence est nécessaire (Zip (), map (), ...). Lorsqu'un objet iterable est transmis en tant qu'argument à la fonction intégrée iter (), il renvoie un itérateur pour l'objet.

Bien sûr, étant donné le style de codage général de Python, basé sur le fait qu'il est «plus facile de demander pardon que d'obtenir une permission», l'attente générale est d'utiliser

try:
    for i in object_in_question:
        do_something
except TypeError:
    do_something_for_non_iterable

Mais si vous avez besoin de le vérifier explicitement, vous pouvez tester un itérable par hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). Vous devez vérifier les deux, car strs n'a pas de méthode __iter__ (du moins pas en Python 2, mais en Python 3) et parce que les objets generator n'ont pas de méthode __getitem__.

10
Anaphory

Depuis Python 3.5, vous pouvez utiliser le module typing de la bibliothèque standard pour les tâches liées au type:

from typing import Iterable

...

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

pandas a une fonction intégrée comme celle-ci:

from pandas.util.testing import isiterable
5
Sören

Je trouve souvent pratique, dans mes scripts, de définir une fonction iterable ..__ (intègre maintenant la simplification suggérée par Alfe):

import collections

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

afin que vous puissiez tester si un objet est éditable sous une forme très lisible

if iterable(obj):
    # act on iterable
else:
    # not iterable

comme vous le feriez avec la fonction callable

EDIT: si vous avez numpy installé, vous pouvez simplement faire: à partir de numpy import iterable, Qui est simplement quelque chose comme

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

Si vous n'avez pas numpy, vous pouvez simplement implémenter ce code ou celui ci-dessus.

5
fmonegaglia
def is_iterable(x):
    try:
        0 in x
    except TypeError:
        return False
    else:
        return True

Cela dira oui à toutes sortes d'objets itérables, mais cela dira non aux chaînes de Python 2. (C'est ce que je veux par exemple lorsqu'une fonction récursive peut prendre une chaîne ou un conteneur de chaînes. Dans cette situation, demander pardon peut conduire à un code obscur, et il est préférable de demander l'autorisation au préalable.)

import numpy

class Yes:
    def __iter__(self):
        yield 1;
        yield 2;
        yield 3;

class No:
    pass

class Nope:
    def __iter__(self):
        return 'nonsense'

assert is_iterable(Yes())
assert is_iterable(range(3))
assert is_iterable((1,2,3))   # Tuple
assert is_iterable([1,2,3])   # list
assert is_iterable({1,2,3})   # set
assert is_iterable({1:'one', 2:'two', 3:'three'})   # dictionary
assert is_iterable(numpy.array([1,2,3]))
assert is_iterable(bytearray("not really a string", 'utf-8'))

assert not is_iterable(No())
assert not is_iterable(Nope())
assert not is_iterable("string")
assert not is_iterable(42)
assert not is_iterable(True)
assert not is_iterable(None)

Beaucoup d'autres stratégies ici diront oui aux chaînes. Utilisez-les si c'est ce que vous voulez.

import collections
import numpy

assert isinstance("string", collections.Iterable)
assert isinstance("string", collections.Sequence)
assert numpy.iterable("string")
assert iter("string")
assert hasattr("string", '__getitem__')

Remarque: is_iterable () dira oui aux chaînes de type bytes et bytearray.

  • Les objets bytes dans Python 3 sont des éléments éditables True == is_iterable(b"string") == is_iterable("string".encode('utf-8')) Il n’existe aucun type de ce type dans Python 2.
  • Les objets bytearray dans Python 2 et 3 sont éditables True == is_iterable(bytearray(b"abc"))

L’approche O.P. hasattr(x, '__iter__') dira oui aux chaînes de Python 3 et non à Python 2 (peu importe si '' ou b'' ou u''). Merci à @LuisMasuelli pour avoir remarqué que cela vous permettra également de tomber sur un bugcode __iter__.

3
Bob Stein

Le moyen le plus simple, en respectant le type duc de canard de Python , est d’attraper l’erreur (Python sait parfaitement ce qu’il attend d’un objet pour devenir un itérateur):

class A(object):
    def __getitem__(self, item):
        return something

class B(object):
    def __iter__(self):
        # Return a compliant iterator. Just an example
        return iter([])

class C(object):
    def __iter__(self):
        # Return crap
        return 1

class D(object): pass

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

assert iterable(A())
assert iterable(B())
assert iterable(C())
assert not iterable(D())

Remarques:

  1. Peu importe que vous fassiez la distinction si l'objet n'est pas itérable ou si un bugcode __iter__ a été implémenté, si le type d'exception est le même: de toute façon, vous ne pourrez pas itérer l'objet.
  2. Je pense comprendre votre inquiétude. Comment callable existe-t-il en tant que contrôle si je peux également compter sur la saisie au moyen de canards pour générer une AttributeError si __call__ n'est pas défini pour mon objet, mais ce n'est pas le cas pour un contrôle itératif?

    Je ne connais pas la réponse, mais vous pouvez implémenter la fonction donnée par moi (ou d’autres utilisateurs), ou tout simplement saisir l’exception dans votre code (votre implémentation dans cette partie ressemblera à celle que j’ai écrite - assurez-vous d’isoler le création d'itérateur à partir du reste du code afin que vous puissiez capturer l'exception et la distinguer d'une autre TypeError.

2
Luis Masuelli

La isiterablefonctionnalité du code suivant renvoie True si l'objet est itérable. si ce n'est pas un résultat itératif False

def isiterable(object_):
    return hasattr(type(object_), "__iter__")

exemple

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
2
Nomad

Au lieu de rechercher l'attribut __iter__, vous pouvez rechercher l'attribut __len__ qui est implémenté par chaque élément python intégré, qui peut être itéré, y compris les chaînes. 

>>> hasattr(1, "__len__")
False
>>> hasattr(1.3, "__len__")
False
>>> hasattr("a", "__len__")
True
>>> hasattr([1,2,3], "__len__")
True
>>> hasattr({1,2}, "__len__")
True
>>> hasattr({"a":1}, "__len__")
True
>>> hasattr(("a", 1), "__len__")
True

Les objets non-iterables ne l'implémenteraient pas pour des raisons évidentes. Cependant, il ne capture pas les itérables définis par l'utilisateur qui ne l'implémentent pas, pas plus que les expressions de générateur, que iter peut traiter. Cependant, cela peut être fait en ligne, et ajouter une simple expression or pour vérifier les générateurs résoudrait ce problème. (Notez que l'écriture type(my_generator_expression) == generator jetterait une NameError. Reportez-vous à this answer à la place.)

Vous pouvez utiliser GeneratorType à partir de types:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True

--- réponse acceptée par utdemir

(Cela le rend utile pour vérifier si vous pouvez appeler len sur l'objet cependant.)

1
DarthCadeus

Il m’a toujours échappé quant aux raisons pour lesquelles python a callable(obj) -> bool mais pas iterable(obj) -> bool...
Il est sûrement plus facile de faire hasattr(obj,'__call__') même si c'est plus lent.

Comme presque toutes les autres réponses recommandent d'utiliser try/except TypeError, où le test des exceptions est généralement considéré comme une mauvaise pratique dans toutes les langues, voici une implémentation de iterable(obj) -> bool que je préfère et que j'utilise souvent:

Pour l'amour de python 2, j'utiliserai un lambda pour améliorer encore plus les performances ...
(en python 3, peu importe ce que vous utilisez pour définir la fonction, def a à peu près la même vitesse que lambda)

iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__')

Notez que cette fonction s'exécute plus rapidement pour les objets avec __iter__ puisqu'elle ne teste pas __getitem__.

La plupart des objets itératifs doivent s'appuyer sur __iter__, les objets à casse spéciale revenant à __getitem__, bien que l'un ou l'autre soit requis pour qu'un objet soit itérable.
(et puisque c'est standard, cela affecte aussi les objets C)

1
Tcll

Un peu tard dans la soirée mais je me suis posé la question et je l’ai alors pensé à une réponse. Je ne sais pas si quelqu'un a déjà posté ceci. Mais, en gros, j'ai remarqué que tous les types itérables ont "getitem" dans leur dict. Voici comment vérifier si un objet est un objet éditable sans même essayer. (Jeu de mots intentionnel)

def is_attr(arg):
    return '__getitem__' in dir(arg)
1
lakam99

Pas vraiment "correct" mais peut servir de vérification rapide des types les plus courants tels que les chaînes, les n-uplets, les flotteurs, etc ...

>>> '__iter__' in dir('sds')
True
>>> '__iter__' in dir(56)
False
>>> '__iter__' in dir([5,6,9,8])
True
>>> '__iter__' in dir({'jh':'ff'})
True
>>> '__iter__' in dir({'jh'})
True
>>> '__iter__' in dir(56.9865)
False
0
Jan Musil