En Python, comment puis-je déterminer si un objet est itératif?
Est-il une méthode comme isiterable
? La seule solution que j'ai trouvé pour l'instant est d'appeler
hasattr(myObj, '__iter__')
Mais je ne suis pas sûr de la façon infaillible de ce qui est.
__getitem__
est également suffisante pour en faire un objet itérable- FWIW:
iter(myObj)
réussit siisinstance(myObj, dict)
, donc si vous êtes à la recherche à unmyObj
que pourrait être une séquence dedict
s ou un seuldict
, vous allez réussir dans les deux cas. Une subtilité qui est important si vous voulez savoir ce qu'est une séquence et ce qui ne l'est pas. (en Python 2) __getitem__
est également suffisante pour en faire un objet itérable ... si elle commence à l'index zéro.
Vous devez vous connecter pour publier un commentaire.
La vérification de
__iter__
travaille sur des types de séquence, mais il ne pourrait pas sur, par exemple, les chaînes de en Python 2. Je voudrais savoir le droit de réponse trop, jusqu'alors, ici, c'est une possibilité (qui serait de travailler sur des chaînes de caractères, aussi):La
iter
contrôles intégrés pour la__iter__
méthode ou, dans le cas des chaînes de la__getitem__
méthode.Un autre général pythonic approche est de supposer un objet iterable, puis échouer normalement si il ne fonctionne pas sur l'objet donné. Le Python glossaire:
La
collections
module fournit certaines classes de base abstraites, qui permettent de poser des classes ou des instances si elles offrent la fonctionnalité particulière, par exemple:Toutefois, cela ne se vérifie pas pour les classes qui sont itératif par
__getitem__
.[e for e in my_object]
peut soulever une exception pour d'autres raisons, c'est à diremy_object
est pas défini des éventuels bugs dansmy_object
mise en œuvre.TypeError
...isinstance('', Sequence) == True
) et que toute séquence est itérable (isinstance('', Iterable)
). Sihasattr('', '__iter__') == False
et il pourrait être source de confusion.my_object
est très grand (disons, infini commeitertools.count()
) votre compréhension de liste va prendre beaucoup de temps/mémoire. Mieux de faire un générateur, qui ne sera jamais essayer de construire un (potentiellement infinie) de la liste.isinstance(e, Iterable)
? Et pourquoi ne vous dire que c'est une autre affaire"? Entendez-vous le vieux-classes de style?TypeError
ne résout pas le problème décrit par Nick ainsi Shaung commentaire dans un sens seulement réitère Pseudo commentaire. Vérification de la description d'une exception comme Arne suggéré est toujours une mauvaise idée que cette description est mise en œuvre en détail.TypeError
peuvent être soulevées à partir des couches plus profondes, et un message d'exception peut changer dans la prochaine version. Je suppose que c'est pourquoi je ne me sens à l'aise avec l'aeap approche lorsque le comportement j'ai le désir est le même quel que soit ce qui a causé l'exception.hasattr(u"hello", '__iter__')
retourneTrue
class X(object): __iter__ = 1
. 3 ne fonctionne pas avec cette.__iter__
sont automatiquement des sous-classes decollections.Iterable
Pourriez-vous nous fournir la référence de cette déclaration?len()
droit? donc, ne serait pas un simpletry: l = len(myObj)
être suffisante pour déterminer simyObj
est un objet iterable?xrange
est un objet qui n'a pas de__iter__
méthode et n'est pas un ordre, mais il est itératif.Duck-typing
Vérification de Type
Utiliser le Les Classes De Base Abstraites. Il leur faut au moins la version 2.6 de Python et de ne travailler que pour les nouvelles classes de style.
Cependant,
iter()
est un peu plus fiable, tel que décrit par la documentation:isinstance(x, (collections.Iterable, collections.Sequence))
au lieu deiter(x)
", note que ce ne seront toujours pas en mesure de détecter un objet iterable objet qui implémente seulement__getitem__
mais pas__len__
. Utilisationiter(x)
et d'intercepter l'exception.J'aimerais apporter un peu plus de lumière sur l'interaction de
iter
,__iter__
et__getitem__
et ce qui se passe derrière les rideaux. Armés de cette connaissance, vous serez en mesure de comprendre pourquoi le meilleur que vous pouvez faire est deJe vais énumérer les faits d'abord et puis un suivi avec un rappel rapide de ce qui se passe quand vous employez une
for
boucle en python, suivie par 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 vraie:une)
o
a un__iter__
méthode qui renvoie un itérateur de l'objet. Un itérateur est un objet avec une__iter__
et un__next__
(Python 2:next
) de la méthode.b)
o
a un__getitem__
méthode.De vérifier qu'une instance de
Iterable
ouSequence
, ou la vérification de laattribut
__iter__
n'est pas assez.Si un objet
o
implémente seulement__getitem__
, mais pas__iter__
,iter(o)
permettra de construire desun itérateur qui tente de récupérer des éléments de
o
par index entier, en commençant à l'indice 0. L'itérateur va attraper lesIndexError
(mais pas d'autres erreurs) qui est soulevé et soulève alorsStopIteration
lui-même.Dans le sens le plus général, il n'y a aucun moyen de vérifier si l'itérateur renvoyé par
iter
est sain d'esprit autre que de l'essayer.Si un objet
o
implémente__iter__
, leiter
fonction assurez-vous queque l'objet retourné par
__iter__
est un itérateur. Il n'y a pas de vérification généralesi un objet implémente uniquement
__getitem__
.__iter__
gagne. Si un objeto
implémente à la fois__iter__
et__getitem__
,iter(o)
appellera__iter__
.Si vous voulez faire vos propres objets itératif, toujours mettre en œuvre les
__iter__
méthode.for
bouclesAfin de suivre le long, vous avez besoin d'une compréhension de ce qui se passe quand vous employez une
for
boucle en Python. Hésitez pas à aller directement à la section suivante si vous savez déjà.Lorsque vous utilisez
for item in o
pour certains itérable objeto
, Python appelsiter(o)
et s'attend à un itérateur objet comme valeur de retour. Un itérateur est un objet qui implémente un__next__
(ounext
en Python 2) de la méthode, et__iter__
méthode.Par convention, le
__iter__
méthode d'un itérateur doit renvoyer l'objet lui-même (c'est à direreturn self
). Python appelle ensuitenext
sur l'itérateur jusqu'àStopIteration
est soulevée. Tout cela se fait de manière implicite, mais la démonstration suivante rend visible:Itération sur un
DemoIterable
:Discussion et illustrations
Sur le point 1 et 2: obtenir un itérateur et peu fiables vérifie
Considérer la classe suivante:
Appel
iter
avec une instance deBasicIterable
retourne un itérateur sans aucun problème parce queBasicIterable
implémente__getitem__
.Cependant, il est important de noter que
b
n'a pas le__iter__
attribut et n'est pas considéré comme une instance deIterable
ouSequence
:C'est pourquoi Couramment Python par Luciano Ramalho recommande d'appeler
iter
et de la manipulation du potentielTypeError
comme le moyen le plus précis pour vérifier si un objet est itératif. Citant directement à partir du livre:Sur le point 3: Itération sur des objets qui ne donnent
__getitem__
, mais pas__iter__
Itération sur une instance de
BasicIterable
fonctionne comme prévu: Pythonconstruit un itérateur qui tente de récupérer des éléments par index, en commençant à zéro, jusqu'à ce qu'un
IndexError
est soulevée. La démo de l'objet__getitem__
méthode retourne simplement leitem
qui a été fourni en tant qu'argument à__getitem__(self, item)
par l'itérateur renvoyé pariter
.Noter que l'itérateur soulève
StopIteration
quand il ne peut pas revenir à l'élément suivant et que leIndexError
qui est soulevé pouritem == 3
est géré en interne. C'est pourquoi en boucle sur uneBasicIterable
avec unfor
boucle fonctionne comme prévu:Voici un autre exemple dans le but de conduire à la maison le concept de la façon dont l'itérateur renvoyé par
iter
essaie de points d'accès par index.WrappedDict
n'hérite pas dedict
, ce qui signifie que les instances n'aurez pas de__iter__
méthode.Noter que les appels à
__getitem__
sont délégués àdict.__getitem__
pour lesquels la notation crochets est tout simplement une abréviation.Sur le point 4 et 5:
iter
vérifie pour un itérateur lorsqu'il appelle__iter__
:Quand
iter(o)
est appelée pour un objeto
,iter
assurez-vous que la valeur de retour de__iter__
, si la méthode est présent, est un itérateur. Cela signifie que l'objet retournédoit mettre en œuvre
__next__
(ounext
en Python 2) et__iter__
.iter
ne peut pas effectuer des vérifications pour les objets qui seulementfournir
__getitem__
, parce qu'il n'a aucun moyen de vérifier si les éléments de l'objet sont accessibles par un index entier.Noter que la construction d'un itérateur de
FailIterIterable
instances échoue immédiatement, tandis que la construction d'un itérateur deFailGetItemIterable
réussit, mais lève une Exception sur le premier appel à__next__
.Sur le point 6:
__iter__
gagneCelui-ci est simple. Si un objet implémente
__iter__
et__getitem__
,iter
appellera__iter__
. Considérons la classe suivanteet la sortie lorsqu'une boucle sur un exemple:
Sur le point 7: votre objet iterable les classes doivent implémenter
__iter__
Vous pourriez vous demander pourquoi la plupart des builtin les séquences comme
list
de mettre en œuvre un__iter__
méthode lorsque__getitem__
serait suffisant.Après tout, l'itération sur les instances de la classe ci-dessus, les délégués des appels à
__getitem__
àlist.__getitem__
(à l'aide de la notation crochets), fonctionnent bien:Les raisons de votre personnalisé iterables devrait mettre en œuvre
__iter__
sont comme suit:__iter__
, les instances seront considérés comme iterables, etisinstance(o, collections.Iterable)
sera de retourTrue
.__iter__
n'est pas un itérateur,iter
échoue immédiatement et élever uneTypeError
.__getitem__
existe en arrière pour des raisons de compatibilité. Citant à nouveau Couramment Python:is_iterable
en retournantTrue
dans letry
bloc etFalse
dans leexcept TypeError
bloc?Ce n'est pas suffisant: l'objet renvoyé par
__iter__
doit mettre en œuvre l'itération protocole (c'est à direnext
méthode). Voir la section correspondante dans le la documentation.En Python, une bonne pratique consiste à "essayer pour voir" au lieu de "vérification".
Ne pas exécuter des vérifications pour voir
si votre canard est vraiment un canardpour voir si il est itératif ou pas, la traiter comme si elle était et de se plaindre, si ce n'était pas le cas.TypeError
et vous jeter hors ici, mais, fondamentalement, oui.as e
" aprèsTypeError
au lieu de par l'ajout de ", e
"?try
est plus rapide queif
, maisexcept
est plus lent queelse
. exception de recherche est généralement lente, peu importe la langue que vous utilisez. Notez que ceci n'inclut pas l'exécution entreelif
etfinally
, où un videtry
/finally
est généralement plus rapide qu'un videif True
/else
. mais être intelligents et d'utiliser ce genre de choses wizely,finally
est généralement utilisé pour le contexte de la gestion plutôt que sur la base de la logique et ne doivent pas être substitués en raison de ce qu'il assure.En Python <= 2.5, vous ne pouvez pas et ne devez - itératif a été un "informel" de l'interface.
Mais depuis Python 2.6 et 3.0, vous pouvez tirer parti de la nouvelle ABC (classe de base abstraite) de l'infrastructure ainsi que quelques builtin Abc, qui sont disponibles dans les collections du module:
Maintenant, si cela est souhaitable ou fonctionne réellement, c'est juste une question de conventions. Comme vous pouvez le voir, vous peut enregistrer un non-itératif objet Itérable - et il déclenche une exception à l'exécution. Par conséquent, isinstance acquiert un "nouveau" sens - il vérifie juste pour "déclaré" type de compatibilité, ce qui est un bon moyen de faire 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:
Si l'objet ne satisfait pas à quoi vous vous attendez, vous venez de lancer une exception TypeError, mais si la bonne ABC a été enregistrée, votre chèque est inutile. Au contraire, si le
__iter__
méthode est disponible Python reconnaîtra automatiquement un objet de cette classe comme étant Itératif.Donc, si vous comptez juste un objet iterable, parcourir et de l'oublier. D'autre part, si vous avez besoin de faire des choses différentes en fonction du type d'entrée, vous pouvez trouver l'ABC de l'infrastructure assez utile.
except:
dans l'exemple de code pour les débutants. Il favorise une mauvaise pratique.La meilleure solution que j'ai trouvé jusqu'à présent:
hasattr(obj, '__contains__')
qui, fondamentalement, vérifie si l'objet implémente l'
in
opérateur.Avantages (aucune des autres solutions a tous les trois):
__iter__
)Notes:
Vous pouvez essayer ceci:
Si nous pouvons faire un générateur qui itère sur elle (mais ne jamais utiliser le générateur pour ne pas prendre de l'espace), il est itératif. Semble comme un "duh" genre de chose. Pourquoi avez-vous besoin pour déterminer si une variable est itératif, en premier lieu?
iterable(itertools.repeat(0))
? 🙂(x for x in a)
crée un générateur, il ne fait pas d'itération sur un.(x for x in a)
précisément équivalent à essayeriterator = iter(a)
? Ou il ya certains cas où les deux sont différents?for _ in a: break
plus simple ? Est-il plus lent ?J'ai trouvé une solution sympa ici:
Depuis Python 3.5 vous pouvez utiliser le en tapant module de la bibliothèque standard pour le type de choses:
Selon la Python 2 Glossaire, iterables sont
Bien sûr, compte tenu du style de codage en Python basé sur le fait que c'est “plus Facile de demander pardon que demander la permission.”, l'attente générale est d'utiliser
Mais si vous avez besoin de vérifier de façon explicite, vous pouvez tester un objet iterable par
hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")
. Vous devez vérifier pour les deux, carstr
s n'ont pas de__iter__
méthode (du moins pas en Python 2, dans Python 3 qu'ils font) et parce quegenerator
objets n'ont pas une__getitem__
méthode.Je trouve souvent pratique, à l'intérieur de mes scripts, afin de définir un
iterable
fonction.(Maintenant doté d'un Alfe suggéré par simplification):
de sorte que vous pouvez tester si un objet est itératif dans le très lisible forme
comme vous le feriez avec le
callable
fonctionEDIT: si vous avez numpy installé, vous pouvez simplement faire: à partir de
numpy import iterable
,qui est tout simplement quelque chose comme
Si vous n'avez pas numpy, vous pouvez simplement mettre en œuvre ce code, ou l'un au-dessus.
if x: return True
else: return False
(avecx
être boolean) vous pouvez écrire ce quereturn x
. Dans votre casreturn isinstance(…)
sansif
.panda a une fonction intégrée comme ça:
Vont dire oui à toutes sortes d'objet iterable objets, mais il dire non aux chaînes de caractères en Python 2. (C'est ce que je veux pour exemple le cas d'une fonction récursive peut prendre une corde ou d'un conteneur de chaînes de caractères. Dans cette situation, pour lui demander pardon peut conduire à obfuscode, et il est préférable de demander la permission d'abord.)
De nombreuses autres stratégies, ici, va dire oui à cordes. Si c'est ce que vous voulez.
Remarque: is_iterable() va dire oui pour les chaînes de type
bytes
etbytearray
.bytes
objets en Python 3 sont itérableTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
Il n'y a pas ce type de Python 2.bytearray
objets en Python 2 et 3 sont itérableTrue == is_iterable(bytearray(b"abc"))
L'O. P.
hasattr(x, '__iter__')
approche va dire oui pour les chaînes de caractères en Python 3 et pas en Python 2 (peu importe si''
oub''
ouu''
). Grâce à @LuisMasuelli pour s'en rendre compte il sera également vous laisser tomber sur un buggy__iter__
.Le moyen le plus facile, le respect de l'Python duck-typing, est d'intercepter l'erreur (Python sait parfaitement à quoi s'attendre à partir d'un objet pour devenir un itérateur):
Notes:
__iter__
a été mis en œuvre, si le type d'exception est le même: de toute façon, vous ne serez pas en mesure d'effectuer une itération de l'objet.Je crois que je comprends votre préoccupation: Comment ne
callable
existe vérifier si je pouvais aussi compter sur duck-typing pour élever unAttributeError
si__call__
n'est pas définie pour mon objet, mais ce n'est pas le cas pour itératif de vérification?Je ne connais pas la réponse, mais vous pouvez soit mettre en œuvre la fonction que j'ai (et d'autres utilisateurs) ont donné, ou tout simplement attraper l'exception dans votre code (votre mise en œuvre dans cette partie sera comme la fonction que j'ai écrit - il suffit de vous assurer d'isoler l'itérateur de la création du reste du code, de sorte que vous pouvez capturer l'exception et de la distinguer d'un autre
TypeError
.C'est toujours échappé moi pourquoi python a
callable(obj) -> bool
mais pasiterable(obj) -> bool
...assurément, il est plus facile de faire
hasattr(obj,'__call__')
même si elle est plus lente.Depuis à peu près tous les autres répondre recommande l'utilisation de
try
/except TypeError
, où le dépistage des exceptions est généralement considéré comme une mauvaise pratique parmi toutes les langues, voici une implémentation deiterable(obj) -> bool
j'ai grandi à éprouver de l'affection et de l'utiliser souvent:Pour python 2 de souci, je vais utiliser une lambda juste pour cette performance boost...
(en python 3, il n'a pas d'importance ce que vous utilisez pour la définition de la fonction,
def
y a à peu près la même vitesse quelambda
)Notez que cette fonction s'exécute plus rapidement pour les objets avec
__iter__
puisqu'il n'a pas fait de test pour__getitem__
.Plus itérable les objets doivent compter sur
__iter__
où spécial objets revenir à__getitem__
, bien que ce soit est nécessaire pour qu'un objet soit itératif.(et puisque c'est la norme, il affecte C objets)
La
isiterable
func au code suivant renvoieTrue
si l'objet est itératif. si ce n'est pas itératif retourneFalse
exemple
Au lieu de vérifier la
__iter__
attribut, vous pouvez vérifier pour la__len__
attribut, qui est mis en œuvre par tous les python builtin itératif, y compris les chaînes.Aucun objet iterable objets ne mettrait pas en œuvre ce pour des raisons évidentes. Cependant, on n'attrape pas définie par l'utilisateur iterables qui ne mettent pas en œuvre, ni générateur d'expressions, qui
iter
peut traiter. Cependant, cela peut être fait en une ligne, et l'ajout d'une simpleor
expression de vérifier les générateurs de résoudre ce problème. (Notez que l'écrituretype(my_generator_expression) == generator
jeter unNameError
. Reportez-vous à cette répondre à la place.)(Ce qui le rend utile pour vérifier si vous pouvez l'appeler
len
sur l'objet.)__len__
... pour ce cas, il est généralement l'utilisation inappropriée de calcul de la distance entre 2 objets. oùobj.dist()
pourrait être facilement remplacé.Pas vraiment "correct", mais peut servir de vérification rapide des types les plus communs comme les chaînes, tuples, flotteurs, etc...
Intervalle régulier de l'essayer et sauf, vous pouvez exécuter l'aider.
aider donnerait toutes les méthodes qui pourraient être exécuté sur cet objet(il pourrait être n'importe quel objet et ne peut pas être une liste par exemple), ce qui est temp dans ce cas.
Remarque: Ce serait quelque chose que tu aimerais faire manuellement.