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.
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__
.
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'
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__
.
try:
iterator = iter(theElement)
except TypeError:
# not iterable
else:
# iterable
# for obj in iterator:
# pass
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'appeleriter(obj)
.
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".
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.
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.
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):
__iter__
)Remarques:
J'ai trouvé une belle solution ici :
isiterable = lambda obj: isinstance(obj, basestring) \
or getattr(obj, '__iter__', False)
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?
Selon le Glossaire Python 2 , les iterables sont
tous les types de séquence (tels que
list
,str
etTuple
) et certains types non séquentiels tels quedict
etfile
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 str
s 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__
.
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)
pandas a une fonction intégrée comme celle-ci:
from pandas.util.testing import isiterable
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.
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
.
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.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__
.
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:
__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.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
.
La isiterable
fonctionnalité 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
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.)
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)
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)
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