__getitem__est également suffisant pour rendre un objet itérable
Kos
4
FWIW: iter(myObj)réussit si isinstance(myObj, dict), donc si vous regardez un myObjqui pourrait être une séquence de dicts ou un seul dict, vous réussirez dans les deux cas. Une subtilité qui est importante si vous voulez savoir ce qu'est une séquence et ce qui ne l'est pas. (en Python 2)
Ben Mosher
7
__getitem__est également suffisant pour rendre un objet itérable ... s'il commence à un index nul .
Carlos A. Gómez
Réponses:
28
J'ai étudié ce problème assez récemment. Sur cette base, ma conclusion est que c'est aujourd'hui la meilleure approche:
from collections.abc importIterable# drop `.abc` with Python 2.7 or lowerdef iterable(obj):return isinstance(obj,Iterable)
Ce qui précède a été recommandé déjà plus tôt, mais le consensus général est que l'utilisation iter()serait préférable:
Nous avons également utilisé iter()notre code à cette fin, mais j'ai récemment commencé à être de plus en plus agacé par des objets qui ne sont __getitem__considérés que comme itérables. Il y a des raisons valables d'avoir __getitem__dans un objet non itérable et avec eux le code ci-dessus ne fonctionne pas bien. Comme exemple réel, nous pouvons utiliser Faker . Le code ci-dessus signale qu'il est itérable mais en réalité, essayer de l'itérer provoque un AttributeError(testé avec 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'
Si nous l'utilisions insinstance(), nous ne considérerions pas accidentellement les instances de Faker (ou tout autre objet n'ayant que __getitem__) comme étant itérables:
Les réponses précédentes ont indiqué que l'utilisation iter()est plus sûre car l'ancienne méthode d'implémentation de l'itération en Python était basée sur __getitem__et l' isinstance()approche ne détectait pas cela. Cela peut avoir été vrai avec les anciennes versions de Python, mais basé sur mes tests assez exhaustifs isinstance()fonctionne très bien de nos jours. Le seul cas où cela isinstance()n'a pas fonctionné mais l' iter()a été avec l' UserDictutilisation de Python 2. Si cela est pertinent, il est possible de l'utiliser isinstance(item, (Iterable, UserDict))pour le couvrir.
Est également typing.Dictconsidéré comme itérable par iter(Dict)mais list(Dict)échoue avec une erreur TypeError: Parameters to generic types must be types. Got 0.. Comme prévu isinstance(Dict, Iterable)retourne faux.
Pekka Klärck
1
Je suis arrivé à la même conclusion, mais pour des raisons différentes. L'utilisation a iterfait ralentir inutilement une partie de notre code qui utilisait la "pré-mise en cache". Si le __iter__code est lent, il en sera de même pour l'appel iter... chaque fois que vous voulez juste voir si quelque chose est itérable.
thorwhalen
843
Vérifier les __iter__travaux sur les types de séquence, mais cela échouerait par exemple sur les chaînes en Python 2 . Je voudrais aussi connaître la bonne réponse, jusque-là, voici une possibilité (qui fonctionnerait aussi sur les chaînes):
from __future__ import print_function
try:
some_object_iterator = iter(some_object)exceptTypeErroras te:print(some_object,'is not iterable')
La fonction iterintégrée vérifie la __iter__méthode ou, dans le cas de chaînes, la __getitem__méthode.
Une autre approche pythonique générale consiste à supposer un itérable, puis à échouer gracieusement s'il ne fonctionne pas sur l'objet donné. Le glossaire Python:
Style de programmation pythonique qui détermine le type d'un objet en inspectant sa méthode ou sa signature d'attribut plutôt que par une relation explicite avec un objet de type ("S'il ressemble à un canard et quacks comme un canard , ce doit être un canard .") En mettant l'accent sur les interfaces plutôt que des types spécifiques, un code bien conçu améliore sa flexibilité en permettant la substitution polymorphe. Le typage canard évite les tests utilisant type () ou isinstance (). Au lieu de cela, il utilise généralement le style de programmation EAFP (Easier to Ask Forgiveness than Permission).
...
try:
_ =(e for e in my_object)exceptTypeError:print my_object,'is not iterable'
Le collectionsmodule 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 importIterableif isinstance(e,Iterable):# e is iterable
Cependant, cela ne vérifie pas les classes qui sont itérables via __getitem__.
[e for e in my_object]peut déclencher une exception pour d'autres raisons, c'est my_object-à- dire des bogues non définis ou possibles dans l' my_objectimplémentation.
Nick Dandoulakis
37
Une chaîne est une séquence ( isinstance('', Sequence) == True) et comme toute séquence, elle est itérable ( isinstance('', Iterable)). Bien hasattr('', '__iter__') == Falseque cela puisse être déroutant.
jfs
82
Si elle my_objectest très grande (disons infinie comme itertools.count()) votre compréhension de la liste prendra beaucoup de temps / mémoire. Mieux vaut faire un générateur, qui n'essaiera jamais de construire une liste (potentiellement infinie).
Chris Lutz
14
Que se passe-t-il si some_object lance également TypeError causé par une autre raison (bogues, etc.)? Comment pouvons-nous le distinguer de la "TypeError non itérable"?
Shaung
54
Notez que dans Python 3: hasattr(u"hello", '__iter__')retoursTrue
Carlos
573
Dactylographie de canard
try:
iterator = iter(theElement)exceptTypeError:# not iterableelse:# iterable# for obj in iterator:# pass
Vérification de type
Utilisez les classes de base abstraites . Ils ont besoin d'au moins Python 2.6 et ne fonctionnent que pour les classes de nouveau style.
from collections.abc importIterable# import directly from collections for Python < 3.3if isinstance(theElement,Iterable):# iterableelse:# not iterable
La vérification isinstance(obj, Iterable)détecte les classes enregistrées comme Iterable ou qui ont une __iter__()méthode, mais elle ne détecte pas les classes qui itèrent avec la __getitem__()
méthode. Le seul moyen fiable de déterminer si un objet est itérable est d'appeler iter(obj).
Extrait de "Fluent Python" de Luciano Ramalho: Depuis Python 3.4, la façon la plus précise de vérifier si un objet x est itérable est d'appeler iter (x) et de gérer une exception TypeError si ce n'est pas le cas. C'est plus précis que d'utiliser isinstance (x, abc.Iterable), car iter (x) prend également en compte la méthode getitem héritée, contrairement à ABC itératif .
RdB
Dans le cas où vous pensez "oh je vais juste isinstance(x, (collections.Iterable, collections.Sequence))au lieu de iter(x)", notez que cela ne détectera toujours pas un objet itérable qui implémente seulement __getitem__mais pas __len__. Utilisez iter(x)et attrapez l'exception.
Dale
Votre deuxième réponse ne fonctionne pas. A PyUNO si je le fais iter(slide1), tout se passe bien, cependant les isinstance(slide1, Iterable)lancers TypeError: issubclass() arg 1 must be a class.
Hi-Angel
@ Hi-Angel sonne comme un bug dans PyUNONotez que votre message d'erreur dit issubclass()au lieu de isinstance().
Georg Schölly
2
Appeler iter () sur un objet peut être une opération coûteuse (voir DataLoader dans Pytorch, qui forge / génère plusieurs processus sur iter ()).
szali
126
Je voudrais jeter un peu plus de lumière sur l'interaction entre iter, __iter__et __getitem__et ce qui se passe derrière les rideaux. Armé de ces connaissances, vous serez en mesure de comprendre pourquoi le mieux que vous puissiez faire est
try:
iter(maybe_iterable)print('iteration will probably work')exceptTypeError:print('not iterable')
Je vais d'abord énumérer les faits et ensuite faire un rappel rapide de ce qui se passe lorsque vous utilisez une forboucle en python, suivi d'une discussion pour illustrer les faits.
Faits
Vous pouvez obtenir un itérateur à partir de n'importe quel objet oen appelant iter(o)si au moins une des conditions suivantes est remplie:
a) opossède une __iter__méthode qui renvoie un objet itérateur. Un itérateur est un objet avec une méthode __iter__et une méthode __next__(Python 2 next:).
b) oa une __getitem__méthode.
La vérification d'une instance de Iterableou Sequence, ou la vérification de l'attribut __iter__ne suffit pas.
Si un objet oimplémente uniquement __getitem__, mais pas __iter__, iter(o)il construira un itérateur qui essaiera d'extraire des éléments à partir od'un index entier, en commençant à l'index 0. L'itérateur interceptera toutes les IndexErrorerreurs (mais aucune autre erreur) qui seront levées, puis se lèvera StopIterationlui-même.
Dans le sens le plus général, il n'y a aucun moyen de vérifier si l'itérateur retourné par iterest sain d'esprit autre que de l'essayer.
Si un objet oimplémente __iter__, la iterfonction s'assurera que l'objet retourné par __iter__est un itérateur. Il n'y a pas de contrôle d'intégrité si un objet est uniquement implémenté __getitem__.
__iter__gagne. Si un objet oimplémente les deux __iter__et __getitem__, iter(o)appellera __iter__.
Si vous souhaitez rendre vos propres objets itérables, implémentez toujours la __iter__méthode.
for boucles
Pour suivre, vous devez comprendre ce qui se passe lorsque vous utilisez une forboucle en Python. N'hésitez pas à passer directement à la section suivante si vous le savez déjà.
Lorsque vous utilisez for item in opour un objet itérable o, Python appelle iter(o)et attend un objet itérateur comme valeur de retour. Un itérateur est tout objet qui implémente une méthode __next__(ou nexten Python 2) et une __iter__méthode.
Par convention, la __iter__méthode d'un itérateur doit retourner l'objet lui-même (ie return self). Python appelle ensuite nextl'itérateur jusqu'à ce qu'il StopIterationsoit levé. Tout cela se produit implicitement, mais la démonstration suivante le rend visible:
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
Itération sur un 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
Discussion et illustrations
Aux points 1 et 2: obtenir un itérateur et des contrôles peu fiables
C'est pourquoi Fluent Python de Luciano Ramalho recommande d'appeler iteret de gérer le potentiel TypeErrorcomme le moyen le plus précis de vérifier si un objet est itérable. Citant directement du livre:
Depuis Python 3.4, la façon la plus précise de vérifier si un objet xest itérable est d'appeler iter(x)et de gérer une TypeErrorexception s'il ne l'est pas. Ceci est plus précis que l'utilisation isinstance(x, abc.Iterable), car iter(x)il considère également la __getitem__méthode héritée , Iterablecontrairement à l' ABC.
Au point 3: itération sur des objets qui ne fournissent que __getitem__, mais pas__iter__
Itération sur une instance de BasicIterabletravaux comme prévu: Python construit un itérateur qui essaie de récupérer des éléments par index, en commençant à zéro, jusqu'à ce que an IndexErrorsoit levé. La __getitem__méthode de l'objet de démonstration renvoie simplement celui itemqui a été fourni comme argument __getitem__(self, item)par l'itérateur renvoyé par 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
Notez que l'itérateur déclenche StopIterationlorsqu'il ne peut pas retourner l'élément suivant et que celui IndexErrorqui est déclenché item == 3est géré en interne. C'est pourquoi le bouclage sur un BasicIterableavec une forboucle fonctionne comme prévu:
>>>for x in b:...print(x)...012
Voici un autre exemple afin de ramener à la maison le concept de la façon dont l'itérateur renvoyé par itertente d'accéder aux éléments par index. WrappedDictn'hérite pas de dict, ce qui signifie que les instances n'auront pas de __iter__méthode.
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
Notez que les appels à __getitem__sont délégués dict.__getitem__pour lesquels la notation entre crochets est simplement un raccourci.
>>> w =WrappedDict({-1:'not printed',...0:'hi',1:'StackOverflow',2:'!',...4:'not printed',...'x':'not printed'})>>>for x in w:...print(x)...
hi
StackOverflow!
Aux points 4 et 5: iterrecherche un itérateur lorsqu'il appelle__iter__ :
Quand iter(o)est appelé pour un objet o, iters'assurera que la valeur de retour de __iter__, si la méthode est présente, est un itérateur. Cela signifie que l'objet renvoyé doit implémenter __next__(ou nexten Python 2) et __iter__. iterne peut effectuer aucun contrôle d'intégrité pour les objets qui fournissent uniquement __getitem__, car il n'a aucun moyen de vérifier si les éléments de l'objet sont accessibles par index entier.
classFailIterIterable(object):def __iter__(self):return object()# not an iteratorclassFailGetitemIterable(object):def __getitem__(self, item):raiseException
Notez que la construction d'un itérateur à partir d' FailIterIterableinstances échoue immédiatement, tandis que la construction d'un itérateur à partir de FailGetItemIterableréussit, mais lèvera une exception lors du premier appel à __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
Au point 6: __iter__gagne
Celui-ci est simple. Si un objet implémente __iter__et __getitem__, iterappellera __iter__. Considérez la classe suivante
>>> iwd =IterWinsDemo()>>>for x in iwd:...print(x)...
__iter__
wins
Au point 7: vos classes itérables devraient implémenter __iter__
Vous pourriez vous demander pourquoi la plupart des séquences intégrées comme listimplémenter une __iter__méthode __getitem__sont suffisantes.
classWrappedList(object):# note: no inheritance from list!def __init__(self, lst):
self._list = lst
def __getitem__(self, item):return self._list[item]
Après tout, l'itération sur les instances de la classe ci-dessus, qui délègue les appels __getitem__à list.__getitem__(en utilisant la notation entre crochets), fonctionnera correctement:
>>> wl =WrappedList(['A','B','C'])>>>for x in wl:...print(x)...
A
B
C
Les raisons pour lesquelles vos itérables personnalisés doivent implémenter __iter__sont les suivantes:
Si vous implémentez __iter__, les instances seront considérées comme itérables et isinstance(o, collections.abc.Iterable)reviendront True.
Si l'objet renvoyé par __iter__n'est pas un itérateur, iteréchouera immédiatement et déclenchera a TypeError.
La gestion spéciale de __getitem__existe pour des raisons de compatibilité descendante. Citant à nouveau de Fluent Python:
C'est pourquoi toute séquence Python est itérable: ils implémentent tous __getitem__. En fait, les séquences standard implémentent également __iter__, et la vôtre devrait aussi, car la gestion spéciale de __getitem__existe pour des raisons de compatibilité descendante et peut être supprimée à l'avenir (bien qu'elle ne soit pas déconseillée au moment où j'écris ceci).
il est donc sûr de définir un prédicat is_iterableen revenant Truedans le trybloc et Falsedans le except TypeErrorbloc?
alancalvitti
C'est une excellente réponse. Je pense que cela met en évidence la nature peu intuitive et malheureuse du protocole getitem. Il n'aurait jamais dû être ajouté.
Neil G
31
Ce n'est pas suffisant: l'objet renvoyé par __iter__doit implémenter le protocole d'itération (ie nextméthode). Voir la section appropriée dans la documentation .
En Python, une bonne pratique consiste à "essayer de voir" au lieu de "vérifier".
@willem: ou "ne demandez pas la permission mais le pardon" ;-)
jldupont
14
@willem Les styles "permission" et "pardon" sont tous deux considérés comme du type canard. Si vous demandez ce qu'un objet peut faire plutôt que ce qu'il est , c'est du typage de canard. Si vous utilisez l'introspection, c'est "permission"; si vous essayez juste de le faire et voyez si cela fonctionne ou non, c'est "le pardon".
Mark Reed
22
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 exploiter la nouvelle infrastructure ABC (classe de base abstraite) ainsi que certains ABC intégrés qui sont disponibles dans le module collections:
from collections importIterableclassMyObject(object):pass
mo =MyObject()print isinstance(mo,Iterable)Iterable.register(MyObject)print isinstance(mo,Iterable)print isinstance("abc",Iterable)
Maintenant, que cela soit souhaitable ou fonctionne réellement, ce n'est qu'une question de conventions. Comme vous pouvez le voir, vous pouvez enregistrer un objet non itérable comme Iterable - et il lèvera une exception lors de l'exécution. Par conséquent, isinstance acquiert une "nouvelle" signification - il vérifie simplement la compatibilité de type "déclaré", ce qui est une bonne façon de procéder en Python.
D'un autre côté, si votre objet ne satisfait pas l'interface dont vous avez besoin, qu'allez-vous faire? Prenons l'exemple suivant:
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
Si l'objet ne satisfait pas ce que vous attendez, vous lancez simplement une TypeError, mais si le bon ABC a été enregistré, votre vérification est inutile. Au contraire, si la __iter__méthode est disponible, Python reconnaîtra automatiquement l'objet de cette classe comme étant itérable.
Donc, si vous vous attendez à un itérable, parcourez-le et oubliez-le. D'un autre côté, si vous devez faire des choses différentes selon le type d'entrée, vous pourriez trouver l'infrastructure ABC assez utile.
n'utilisez pas bare except:dans l'exemple de code pour les débutants. Il favorise les mauvaises pratiques.
jfs
JFS: Je ne le ferais pas, mais je devais passer par plusieurs codes déclencheurs d'exceptions et je ne voulais pas intercepter l'exception spécifique ... Je pense que le but de ce code est assez clair.
Alan Franzoni
21
try:#treat object as iterableexceptTypeError, e:#object is not actually iterable
N'effectuez pas de vérifications pour voir si votre canard est vraiment un canard pour voir s'il est itérable ou non, traitez-le comme s'il l'était et plaignez-vous s'il ne l'est pas.
Techniquement, au cours de l'itération, votre calcul pourrait jeter un TypeErroret vous jeter ici, mais fondamentalement oui.
Chris Lutz
6
@willem: Veuillez utiliser timeit pour effectuer un benchmark. Les exceptions Python sont souvent plus rapides que les instructions if. Ils peuvent emprunter un chemin légèrement plus court à travers l'interprète.
S.Lott
2
@willem: IronPython a des exceptions lentes (par rapport à CPython).
jfs
2
Un essai de travail: la déclaration est vraiment rapide. Donc, si vous avez quelques exceptions, essayez-sauf est rapide. Si vous vous attendez à de nombreuses exceptions, «si» peut être plus rapide.
Arne Babenhauserheide
2
Est-ce que l'objet d'exception ne doit pas être capturé en ajoutant " as e" après TypeErrorau lieu d'ajouter " , e"?
HelloGoodbye
21
Depuis Python 3.5, vous pouvez utiliser le module de saisie de la bibliothèque standard pour des choses liées au type:
from typing importIterable...if isinstance(my_item,Iterable):print(True)
La meilleure solution que j'ai trouvée jusqu'à présent:
hasattr(obj, '__contains__')
qui vérifie essentiellement si l'objet implémente l' inopérateur.
Avantages (aucune des autres solutions n'a les trois):
c'est une expression (fonctionne comme un lambda , par opposition à l' essai ... sauf variante)
il est (devrait être) implémenté par tous les itérables, y compris les 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 itérables et des non-itérables et que vous devez traiter chaque élément différemment selon son type (traiter les itérables à l'essai et non iterables sur l' exception ne fonctionne, mais il regarderaient bout à bout laid et trompeur)
des solutions à ce problème qui tentent d'itérer réellement sur l'objet (par exemple [x pour x dans obj]) pour vérifier s'il est itérable peuvent induire des pénalités de performances importantes pour les grands itérables (surtout si vous avez juste besoin des premiers éléments de l'itérable, par exemple exemple) et doit être évité
Il est plus court (et ne nécessite pas d'importations supplémentaires) sans perdre de clarté: avoir une méthode "contient" semble être un moyen naturel de vérifier si quelque chose est une collection d'objets.
Vlad
46
Ce n'est pas parce que quelque chose peut contenir quelque chose qu'il est itérable. Par exemple, un utilisateur peut vérifier si un point se trouve dans un cube 3D, mais comment voulez-vous parcourir cet objet?
Casey Kuball
13
Ceci est une erreur. Un itérable lui-même ne prend pas en charge contient , au moins avec Python 3.4.
Peter Shinners
15
Vous pouvez essayer ceci:
def iterable(a):try:(x for x in a)returnTrueexceptTypeError:returnFalse
Si nous pouvons créer un générateur qui itère dessus (mais n'utilisez jamais le générateur pour qu'il ne prenne pas de place), c'est itérable. On dirait une sorte de chose "duh". Pourquoi avez-vous besoin de déterminer si une variable est itérable en premier lieu?
@badp, (x for x in a)crée juste un générateur, il ne fait aucune itération sur a.
catchmeifyoutry
5
Essayer est-il (x for x in a)exactement équivalent à essayer iterator = iter(a)? Ou y a-t-il des cas où les deux sont différents?
max
N'est-ce pas for _ in a: breakplus simple? Est-ce plus lent?
Mr_and_Mrs_D
2
@Mr_and_Mrs_D c'est mauvais si l'objet testé est un itérateur qui est itéré par la suite (il y aura 1 élément court car sa position ne peut pas être réinitialisée), la création de générateurs de déchets n'itère pas sur l'objet car ils ne sont pas itérés, bien que je ne sois pas certain qu'il soulèvera 100% une TypeError s'il n'est pas itérable.
tous les types de séquence (tels que list,, stret tuple) et certains types de non-séquence comme dictet fileet les objets de toutes les classes que vous définissez avec une méthode __iter__()ou __getitem__(). Les itérables peuvent être utilisés dans une boucle for et dans de nombreux autres endroits où une séquence est nécessaire (zip (), map (), ...). Lorsqu'un objet itérable est passé 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 pour Python basé sur le fait qu'il est "plus facile de demander pardon que permission.", L'attente générale est d'utiliser
try:for i in object_in_question:
do_something
exceptTypeError:
do_something_for_non_iterable
Mais si vous devez 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 les strs n'ont pas de __iter__méthode (du moins pas en Python 2, en Python 3 ils en ont) et parce que les generatorobjets n'ont pas de __getitem__méthode.
Chaque fois que vous aimez if x: return Trueelse: return False(en xétant booléen), vous pouvez écrire ceci comme return x. Dans votre cas return isinstance(…)sans aucun if.
Alfe
Puisque vous reconnaissez que la solution d'Alfe est meilleure, pourquoi n'avez-vous pas modifié votre réponse pour simplement dire cela? Au lieu de cela, vous avez maintenant les deux versions dans votre réponse. Verbosité inutile. Soumettre une modification pour résoudre ce problème.
ToolmakerSteve
2
Vous devez intercepter "TypeError" dans la ligne `except: return False`. Tout attraper est un mauvais schéma.
Mariusz Jamro
Sache que. J'ai traduit ce morceau de code à partir de la bibliothèque NumPy, qui utilise l'exception générique.
fmonegaglia
Ce n'est pas parce qu'un code est extrait de NumPy qu'il est bon ... modèle ou non, que la seule fois où tout doit être fait doit être si vous gérez explicitement les erreurs dans votre programme.
cependant, cela regarde simplement s'il y a des __iter__séquences et similaires et ne s'en soucie pas vraiment.
ead
4
Il m'a toujours échappé pourquoi le python a callable(obj) -> boolmais pas iterable(obj) -> bool...
c'est sûrement plus facile à fairehasattr(obj,'__call__') même s'il est plus lent.
Étant donné qu'à peu près 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 n'importe quelle langue, voici une implémentation de que iterable(obj) -> boolj'apprécie et utilise souvent:
Pour l'amour de python 2, je vais utiliser un lambda juste pour augmenter les performances supplémentaires ...
(en python 3, peu importe ce que vous utilisez pour définir la fonction, defa à peu près la même vitesse que lambda)
Notez que cette fonction s'exécute plus rapidement pour les objets avec __iter__car elle ne teste pas __getitem__.
La plupart des objets itérables doivent se baser sur l' __iter__endroit où se replient les objets spéciaux __getitem__, bien que l'un ou l'autre soit requis pour qu'un objet soit itérable.
(et comme c'est standard, cela affecte également les objets C)
il ne fournit pas de code de travail, sans parler des performances de python ... bien que cette réponse soit vraiment juste pour plus de commodité comme je l'ai vu à plusieurs reprises ici.
Tcll
3
def is_iterable(x):try:0in x
exceptTypeError:returnFalseelse:returnTrue
Cela dira oui à toutes sortes d'objets itérables, mais cela dira non aux chaînes en 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 obfuscode, et il vaut mieux demander la permission d'abord.)
Remarque: is_iterable () dira oui aux chaînes de type byteset bytearray.
bytesles objets en Python 3 sont itérables True == is_iterable(b"string") == is_iterable("string".encode('utf-8'))Il n'y a pas un tel type en Python 2.
bytearray les objets en Python 2 et 3 sont itérables True == is_iterable(bytearray(b"abc"))
L' hasattr(x, '__iter__')approche OP dira oui aux chaînes en Python 3 et non en Python 2 (que ce soit ''ou b''ou u''). Merci à @LuisMasuelli d'avoir remarqué qu'il vous laissera également tomber sur un buggy __iter__.
Le moyen le plus simple, en respectant le typage canard de Python , est de rattraper 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 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())
Remarques :
Peu importe que l'objet ne soit pas itérable, ou qu'un buggy __iter__ait é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 que je comprends votre préoccupation: comment callableexiste- t- il comme vérification si je pouvais également compter sur la saisie de canard pour élever un AttributeErrorif qui __call__n'est pas défini pour mon objet, mais ce n'est pas le cas pour la vérification itérable?
Je ne connais pas la réponse, mais vous pouvez soit implémenter la fonction que j'ai (et d'autres utilisateurs) donnée, soit simplement intercepter l'exception dans votre code (votre implémentation dans cette partie sera comme la fonction que j'ai écrite - assurez-vous simplement 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.
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
tant de réponses détaillées ci-dessus avec beaucoup de votes positifs et vous jetez une réponse inexpliquée ... meh
Nrzonline
Veuillez ne pas publier de code nu. Incluez également une explication de ce que cela fait.
Jonathan Mee
1
Au lieu de vérifier l' __iter__attribut, vous pouvez vérifier l' __len__attribut, qui est implémenté par chaque itérable intégré python, y compris les chaînes.
Les objets non itérables ne l'implémentent pas pour des raisons évidentes. Cependant, il n'attrape pas les itérables définis par l'utilisateur qui ne l'implémentent pas, ni les expressions de générateur, qui iterpeuvent les gérer. Cependant, cela peut être fait en ligne, et l'ajout d'une orexpression simple vérifiant les générateurs résoudrait ce problème. (Notez que l'écriture type(my_generator_expression) == generatordonnerait un NameError. Reportez - vous plutôt à cette réponse.)
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 lenl'objet cependant.)
Malheureusement, tous les objets itérables ne sont pas utilisés __len__... dans ce cas, c'est généralement une mauvaise utilisation du calcul de la distance entre 2 objets. où obj.dist()pourrait être facilement substitué.
Tcll
Ouais. La plupart des itérables définis par l'utilisateur implémentent iter et getitem mais pas len. Cependant, les types intégrés le font, et si vous voulez vérifier si vous pouvez appeler la fonction len dessus, la vérification de len est plus sécurisée. Mais tu as raison.
DarthCadeus
0
Pas vraiment "correct" mais peut servir de vérification rapide des types les plus courants comme les cordes, les tuples, les flotteurs, etc ...
Un peu tard pour la fête mais je me suis posé cette question et j'ai vu cela puis j'ai pensé à une réponse. Je ne sais pas si quelqu'un a déjà posté ça. Mais essentiellement, j'ai remarqué que tous les types itérables ont __getitem __ () dans leur dict. C'est ainsi que vous vérifieriez si un objet est un itérable sans même essayer. (Jeu de mots volontaire)
__getitem__
est également suffisant pour rendre un objet itérableiter(myObj)
réussit siisinstance(myObj, dict)
, donc si vous regardez unmyObj
qui pourrait être une séquence dedict
s ou un seuldict
, vous réussirez dans les deux cas. Une subtilité qui est importante si vous voulez savoir ce qu'est une séquence et ce qui ne l'est pas. (en Python 2)__getitem__
est également suffisant pour rendre un objet itérable ... s'il commence à un index nul .Réponses:
J'ai étudié ce problème assez récemment. Sur cette base, ma conclusion est que c'est aujourd'hui la meilleure approche:
Ce qui précède a été recommandé déjà plus tôt, mais le consensus général est que l'utilisation
iter()
serait préférable:Nous avons également utilisé
iter()
notre code à cette fin, mais j'ai récemment commencé à être de plus en plus agacé par des objets qui ne sont__getitem__
considérés que comme itérables. Il y a des raisons valables d'avoir__getitem__
dans un objet non itérable et avec eux le code ci-dessus ne fonctionne pas bien. Comme exemple réel, nous pouvons utiliser Faker . Le code ci-dessus signale qu'il est itérable mais en réalité, essayer de l'itérer provoque unAttributeError
(testé avec Faker 4.0.2):Si nous l'utilisions
insinstance()
, nous ne considérerions pas accidentellement les instances de Faker (ou tout autre objet n'ayant que__getitem__
) comme étant itérables:Les réponses précédentes ont indiqué que l'utilisation
iter()
est plus sûre car l'ancienne méthode d'implémentation de l'itération en Python était basée sur__getitem__
et l'isinstance()
approche ne détectait pas cela. Cela peut avoir été vrai avec les anciennes versions de Python, mais basé sur mes tests assez exhaustifsisinstance()
fonctionne très bien de nos jours. Le seul cas où celaisinstance()
n'a pas fonctionné mais l'iter()
a été avec l'UserDict
utilisation de Python 2. Si cela est pertinent, il est possible de l'utiliserisinstance(item, (Iterable, UserDict))
pour le couvrir.la source
typing.Dict
considéré comme itérable pariter(Dict)
maislist(Dict)
échoue avec une erreurTypeError: Parameters to generic types must be types. Got 0.
. Comme prévuisinstance(Dict, Iterable)
retourne faux.iter
fait ralentir inutilement une partie de notre code qui utilisait la "pré-mise en cache". Si le__iter__
code est lent, il en sera de même pour l'appeliter
... chaque fois que vous voulez juste voir si quelque chose est itérable.Vérifier les
__iter__
travaux sur les types de séquence, mais cela échouerait par exemple sur les chaînes en Python 2 . Je voudrais aussi connaître la bonne réponse, jusque-là, voici une possibilité (qui fonctionnerait aussi sur les chaînes):La fonction
iter
intégrée vérifie la__iter__
méthode ou, dans le cas de chaînes, la__getitem__
méthode.Une autre approche pythonique générale consiste à supposer un itérable, puis à échouer gracieusement s'il ne fonctionne pas sur l'objet donné. Le glossaire Python:
Le
collections
module 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:Cependant, cela ne vérifie pas les classes qui sont itérables via
__getitem__
.la source
[e for e in my_object]
peut déclencher une exception pour d'autres raisons, c'estmy_object
-à- dire des bogues non définis ou possibles dans l'my_object
implémentation.isinstance('', Sequence) == True
) et comme toute séquence, elle est itérable (isinstance('', Iterable)
). Bienhasattr('', '__iter__') == False
que cela puisse être déroutant.my_object
est très grande (disons infinie commeitertools.count()
) votre compréhension de la liste prendra beaucoup de temps / mémoire. Mieux vaut faire un générateur, qui n'essaiera jamais de construire une liste (potentiellement infinie).hasattr(u"hello", '__iter__')
retoursTrue
Dactylographie de canard
Vérification de type
Utilisez les classes de base abstraites . Ils ont besoin d'au moins Python 2.6 et ne fonctionnent que pour les classes de nouveau style.
Cependant,
iter()
est un peu plus fiable comme décrit dans la documentation :la source
isinstance(x, (collections.Iterable, collections.Sequence))
au lieu deiter(x)
", notez que cela ne détectera toujours pas un objet itérable qui implémente seulement__getitem__
mais pas__len__
. Utiliseziter(x)
et attrapez l'exception.iter(slide1)
, tout se passe bien, cependant lesisinstance(slide1, Iterable)
lancersTypeError: issubclass() arg 1 must be a class
.PyUNO
Notez que votre message d'erreur ditissubclass()
au lieu deisinstance()
.Je voudrais jeter un peu plus de lumière sur l'interaction entre
iter
,__iter__
et__getitem__
et ce qui se passe derrière les rideaux. Armé de ces connaissances, vous serez en mesure de comprendre pourquoi le mieux que vous puissiez faire estJe vais d'abord énumérer les faits et ensuite faire un rappel rapide de ce qui se passe lorsque vous utilisez une
for
boucle en python, suivi d'une discussion pour illustrer les faits.Faits
Vous pouvez obtenir un itérateur à partir de n'importe quel objet
o
en appelantiter(o)
si au moins une des conditions suivantes est remplie:a)
o
possède une__iter__
méthode qui renvoie un objet itérateur. Un itérateur est un objet avec une méthode__iter__
et une méthode__next__
(Python 2next
:).b)
o
a une__getitem__
méthode.La vérification d'une instance de
Iterable
ouSequence
, ou la vérification de l'attribut__iter__
ne suffit pas.Si un objet
o
implémente uniquement__getitem__
, mais pas__iter__
,iter(o)
il construira un itérateur qui essaiera d'extraire des éléments à partiro
d'un index entier, en commençant à l'index 0. L'itérateur interceptera toutes lesIndexError
erreurs (mais aucune autre erreur) qui seront levées, puis se lèveraStopIteration
lui-même.Dans le sens le plus général, il n'y a aucun moyen de vérifier si l'itérateur retourné par
iter
est sain d'esprit autre que de l'essayer.Si un objet
o
implémente__iter__
, laiter
fonction s'assurera que l'objet retourné par__iter__
est un itérateur. Il n'y a pas de contrôle d'intégrité si un objet est uniquement implémenté__getitem__
.__iter__
gagne. Si un objeto
implémente les deux__iter__
et__getitem__
,iter(o)
appellera__iter__
.Si vous souhaitez rendre vos propres objets itérables, implémentez toujours la
__iter__
méthode.for
bouclesPour suivre, vous devez comprendre ce qui se passe lorsque vous utilisez une
for
boucle en Python. N'hésitez pas à passer directement à la section suivante si vous le savez déjà.Lorsque vous utilisez
for item in o
pour un objet itérableo
, Python appelleiter(o)
et attend un objet itérateur comme valeur de retour. Un itérateur est tout objet qui implémente une méthode__next__
(ounext
en Python 2) et une__iter__
méthode.Par convention, la
__iter__
méthode d'un itérateur doit retourner l'objet lui-même (iereturn self
). Python appelle ensuitenext
l'itérateur jusqu'à ce qu'ilStopIteration
soit levé. Tout cela se produit implicitement, mais la démonstration suivante le rend visible:Itération sur un
DemoIterable
:Discussion et illustrations
Aux points 1 et 2: obtenir un itérateur et des contrôles peu fiables
Considérez la classe suivante:
L'appel
iter
avec une instance deBasicIterable
retournera un itérateur sans aucun problème carBasicIterable
implémente__getitem__
.Cependant, il est important de noter qu'il
b
n'a pas l'__iter__
attribut et n'est pas considéré comme une instance deIterable
ouSequence
:C'est pourquoi Fluent Python de Luciano Ramalho recommande d'appeler
iter
et de gérer le potentielTypeError
comme le moyen le plus précis de vérifier si un objet est itérable. Citant directement du livre:Au point 3: itération sur des objets qui ne fournissent que
__getitem__
, mais pas__iter__
Itération sur une instance de
BasicIterable
travaux comme prévu: Python construit un itérateur qui essaie de récupérer des éléments par index, en commençant à zéro, jusqu'à ce que anIndexError
soit levé. La__getitem__
méthode de l'objet de démonstration renvoie simplement celuiitem
qui a été fourni comme argument__getitem__(self, item)
par l'itérateur renvoyé pariter
.Notez que l'itérateur déclenche
StopIteration
lorsqu'il ne peut pas retourner l'élément suivant et que celuiIndexError
qui est déclenchéitem == 3
est géré en interne. C'est pourquoi le bouclage sur unBasicIterable
avec unefor
boucle fonctionne comme prévu:Voici un autre exemple afin de ramener à la maison le concept de la façon dont l'itérateur renvoyé par
iter
tente d'accéder aux éléments par index.WrappedDict
n'hérite pas dedict
, ce qui signifie que les instances n'auront pas de__iter__
méthode.Notez que les appels à
__getitem__
sont déléguésdict.__getitem__
pour lesquels la notation entre crochets est simplement un raccourci.Aux points 4 et 5:
iter
recherche un itérateur lorsqu'il appelle__iter__
:Quand
iter(o)
est appelé pour un objeto
,iter
s'assurera que la valeur de retour de__iter__
, si la méthode est présente, est un itérateur. Cela signifie que l'objet renvoyé doit implémenter__next__
(ounext
en Python 2) et__iter__
.iter
ne peut effectuer aucun contrôle d'intégrité pour les objets qui fournissent uniquement__getitem__
, car il n'a aucun moyen de vérifier si les éléments de l'objet sont accessibles par index entier.Notez que la construction d'un itérateur à partir d'
FailIterIterable
instances échoue immédiatement, tandis que la construction d'un itérateur à partir deFailGetItemIterable
réussit, mais lèvera une exception lors du premier appel à__next__
.Au point 6:
__iter__
gagneCelui-ci est simple. Si un objet implémente
__iter__
et__getitem__
,iter
appellera__iter__
. Considérez la classe suivanteet la sortie lors du bouclage sur une instance:
Au point 7: vos classes itérables devraient implémenter
__iter__
Vous pourriez vous demander pourquoi la plupart des séquences intégrées comme
list
implémenter une__iter__
méthode__getitem__
sont suffisantes.Après tout, l'itération sur les instances de la classe ci-dessus, qui délègue les appels
__getitem__
àlist.__getitem__
(en utilisant la notation entre crochets), fonctionnera correctement:Les raisons pour lesquelles vos itérables personnalisés doivent implémenter
__iter__
sont les suivantes:__iter__
, les instances seront considérées comme itérables etisinstance(o, collections.abc.Iterable)
reviendrontTrue
.__iter__
n'est pas un itérateur,iter
échouera immédiatement et déclenchera aTypeError
.__getitem__
existe pour des raisons de compatibilité descendante. Citant à nouveau de Fluent Python:la source
is_iterable
en revenantTrue
dans letry
bloc etFalse
dans leexcept TypeError
bloc?Ce n'est pas suffisant: l'objet renvoyé par
__iter__
doit implémenter le protocole d'itération (ienext
méthode). Voir la section appropriée dans la documentation .En Python, une bonne pratique consiste à "essayer de voir" au lieu de "vérifier".
la source
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 exploiter la nouvelle infrastructure ABC (classe de base abstraite) ainsi que certains ABC intégrés qui sont disponibles dans le module collections:
Maintenant, que cela soit souhaitable ou fonctionne réellement, ce n'est qu'une question de conventions. Comme vous pouvez le voir, vous pouvez enregistrer un objet non itérable comme Iterable - et il lèvera une exception lors de l'exécution. Par conséquent, isinstance acquiert une "nouvelle" signification - il vérifie simplement la compatibilité de type "déclaré", ce qui est une bonne façon de procéder en Python.
D'un autre côté, si votre objet ne satisfait pas l'interface dont vous avez besoin, qu'allez-vous faire? Prenons l'exemple suivant:
Si l'objet ne satisfait pas ce que vous attendez, vous lancez simplement une TypeError, mais si le bon ABC a été enregistré, votre vérification est inutile. Au contraire, si la
__iter__
méthode est disponible, Python reconnaîtra automatiquement l'objet de cette classe comme étant itérable.Donc, si vous vous attendez à un itérable, parcourez-le et oubliez-le. D'un autre côté, si vous devez faire des choses différentes selon le type d'entrée, vous pourriez trouver l'infrastructure ABC assez utile.
la source
except:
dans l'exemple de code pour les débutants. Il favorise les mauvaises pratiques.N'effectuez pas de vérifications pour voir
si votre canard est vraiment un canardpour voir s'il est itérable ou non, traitez-le comme s'il l'était et plaignez-vous s'il ne l'est pas.la source
TypeError
et vous jeter ici, mais fondamentalement oui.as e
" aprèsTypeError
au lieu d'ajouter ", e
"?Depuis Python 3.5, vous pouvez utiliser le module de saisie de la bibliothèque standard pour des choses liées au type:
la source
La meilleure solution que j'ai trouvée jusqu'à présent:
hasattr(obj, '__contains__')
qui vérifie essentiellement si l'objet implémente l'
in
opérateur.Avantages (aucune des autres solutions n'a les trois):
__iter__
)Remarques:
la source
Vous pouvez essayer ceci:
Si nous pouvons créer un générateur qui itère dessus (mais n'utilisez jamais le générateur pour qu'il ne prenne pas de place), c'est itérable. On dirait une sorte de chose "duh". Pourquoi avez-vous besoin de déterminer si une variable est itérable en premier lieu?
la source
iterable(itertools.repeat(0))
? :)(x for x in a)
crée juste un générateur, il ne fait aucune itération sur a.(x for x in a)
exactement équivalent à essayeriterator = iter(a)
? Ou y a-t-il des cas où les deux sont différents?for _ in a: break
plus simple? Est-ce plus lent?J'ai trouvé une bonne solution ici :
la source
Selon le glossaire Python 2 , les itérables sont
Bien sûr, étant donné le style de codage général pour Python basé sur le fait qu'il est "plus facile de demander pardon que permission.", L'attente générale est d'utiliser
Mais si vous devez 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 lesstr
s n'ont pas de__iter__
méthode (du moins pas en Python 2, en Python 3 ils en ont) et parce que lesgenerator
objets n'ont pas de__getitem__
méthode.la source
Je trouve souvent pratique, dans mes scripts, de définir une
iterable
fonction. (Comprend maintenant la simplification suggérée par Alfe):afin que vous puissiez tester si un objet est itérable sous une forme très lisible
comme vous le feriez avec le
callable
fonctionEDIT: si vous avez installé numpy, vous pouvez simplement faire: from
numpy import iterable
, qui est simplement quelque chose commeSi vous n'avez pas numpy, vous pouvez simplement implémenter ce code, ou celui ci-dessus.
la source
if x: return True
else: return False
(enx
étant booléen), vous pouvez écrire ceci commereturn x
. Dans votre casreturn isinstance(…)
sans aucunif
.pandas a une fonction intégrée comme ça:
la source
__iter__
séquences et similaires et ne s'en soucie pas vraiment.Il m'a toujours échappé pourquoi le python a
callable(obj) -> bool
mais pasiterable(obj) -> bool
...c'est sûrement plus facile à faire
hasattr(obj,'__call__')
même s'il est plus lent.Étant donné qu'à peu près 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 n'importe quelle langue, voici une implémentation de queiterable(obj) -> bool
j'apprécie et utilise souvent:Pour l'amour de python 2, je vais utiliser un lambda juste pour augmenter les performances supplémentaires ...
(en python 3, peu importe ce que vous utilisez pour définir la fonction,
def
a à peu près la même vitesse quelambda
)Notez que cette fonction s'exécute plus rapidement pour les objets avec
__iter__
car elle ne teste pas__getitem__
.La plupart des objets itérables doivent se baser sur l'
__iter__
endroit où se replient les objets spéciaux__getitem__
, bien que l'un ou l'autre soit requis pour qu'un objet soit itérable.(et comme c'est standard, cela affecte également les objets C)
la source
Cela dira oui à toutes sortes d'objets itérables, mais cela dira non aux chaînes en 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 obfuscode, et il vaut mieux demander la permission d'abord.)
De nombreuses autres stratégies ici diront oui aux chaînes. Utilisez-les si c'est ce que vous voulez.
Remarque: is_iterable () dira oui aux chaînes de type
bytes
etbytearray
.bytes
les objets en Python 3 sont itérablesTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
Il n'y a pas un tel type en Python 2.bytearray
les objets en Python 2 et 3 sont itérablesTrue == is_iterable(bytearray(b"abc"))
L'
hasattr(x, '__iter__')
approche OP dira oui aux chaînes en Python 3 et non en Python 2 (que ce soit''
oub''
ouu''
). Merci à @LuisMasuelli d'avoir remarqué qu'il vous laissera également tomber sur un buggy__iter__
.la source
Le moyen le plus simple, en respectant le typage canard de Python , est de rattraper l'erreur (Python sait parfaitement ce qu'il attend d'un objet pour devenir un itérateur):
Remarques :
__iter__
ait é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 que je comprends votre préoccupation: comment
callable
existe- t- il comme vérification si je pouvais également compter sur la saisie de canard pour élever unAttributeError
if qui__call__
n'est pas défini pour mon objet, mais ce n'est pas le cas pour la vérification itérable?Je ne connais pas la réponse, mais vous pouvez soit implémenter la fonction que j'ai (et d'autres utilisateurs) donnée, soit simplement intercepter l'exception dans votre code (votre implémentation dans cette partie sera comme la fonction que j'ai écrite - assurez-vous simplement 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 source
La
isiterable
fonction du code suivant retourneTrue
si l'objet est itérable. si ce n'est pas des retours itérablesFalse
exemple
la source
Au lieu de vérifier l'
__iter__
attribut, vous pouvez vérifier l'__len__
attribut, qui est implémenté par chaque itérable intégré python, y compris les chaînes.Les objets non itérables ne l'implémentent pas pour des raisons évidentes. Cependant, il n'attrape pas les itérables définis par l'utilisateur qui ne l'implémentent pas, ni les expressions de générateur, qui
iter
peuvent les gérer. Cependant, cela peut être fait en ligne, et l'ajout d'uneor
expression simple vérifiant les générateurs résoudrait ce problème. (Notez que l'écrituretype(my_generator_expression) == generator
donnerait unNameError
. Reportez - vous plutôt à cette réponse.)(Cela le rend utile pour vérifier si vous pouvez appeler
len
l'objet cependant.)la source
__len__
... dans ce cas, c'est généralement une mauvaise utilisation du calcul de la distance entre 2 objets. oùobj.dist()
pourrait être facilement substitué.Pas vraiment "correct" mais peut servir de vérification rapide des types les plus courants comme les cordes, les tuples, les flotteurs, etc ...
la source
Un peu tard pour la fête mais je me suis posé cette question et j'ai vu cela puis j'ai pensé à une réponse. Je ne sais pas si quelqu'un a déjà posté ça. Mais essentiellement, j'ai remarqué que tous les types itérables ont __getitem __ () dans leur dict. C'est ainsi que vous vérifieriez si un objet est un itérable sans même essayer. (Jeu de mots volontaire)
la source