python iterable

title in python plot



En Python, ¿cómo puedo determinar si un objeto es iterable? (19)

Escribiendo pato

try: iterator = iter(theElement) except TypeError: # not iterable else: # iterable # for obj in iterator: # pass

Verificación de tipos

Usa las clases base abstractas . Necesitan al menos Python 2.6 y funcionan solo para clases de nuevo estilo.

from collections.abc import Iterable # import directly from collections for Python < 3.3 if isinstance(theElement, Iterable): # iterable else: # not iterable

Sin embargo, iter() es un poco más confiable como lo describe la documentación :

Al verificar isinstance(obj, Iterable) detecta las clases que están registradas como Iterable o que tienen un __iter__() , pero no detecta las clases que se repiten con el método __getitem__() . La única forma confiable de determinar si un objeto es iterable es llamar a iter(obj) .

¿Hay un método como isiterable ? La única solución que he encontrado hasta ahora es llamar

hasattr(myObj, ''__iter__'')

Pero no estoy seguro de cuán infalible es esto.


  1. La comprobación de __iter__ funciona en tipos de secuencia, pero fallaría en, por ejemplo, cadenas en Python 2 . También me gustaría saber la respuesta correcta, hasta entonces, aquí hay una posibilidad (que también funcionaría en cadenas):

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

    El iter incorporado comprueba el método __iter__ o, en el caso de cadenas, el método __getitem__ .

  2. Otro enfoque pitónico general es asumir un iterable, luego fallar con gracia si no funciona en el objeto dado. El glosario de Python:

    Estilo de programación Pythonic que determina el tipo de un objeto mediante la inspección de su método o firma de atributo en lugar de hacerlo mediante una relación explícita con algún tipo de objeto ("Si se parece a un pato y a los curanderos como a un pato , debe ser un pato ".) Enfatizando las interfaces En lugar de tipos específicos, el código bien diseñado mejora su flexibilidad al permitir la sustitución polimórfica. Duck-typing evita las pruebas que usan type () o isinstance (). En su lugar, generalmente emplea el estilo de programación EAFP (Más fácil de perdonar que el permiso).

    ...

    try: _ = (e for e in my_object) except TypeError: print my_object, ''is not iterable''

  3. El módulo de collections proporciona algunas clases básicas abstractas, que permiten preguntar clases o instancias si proporcionan una funcionalidad particular, por ejemplo:

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

    Sin embargo, esto no comprueba las clases que son iterables a través de __getitem__ .


A menudo me parece conveniente, dentro de mis scripts, definir una función iterable . (Ahora incorpora la simplificación sugerida de Alfe):

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

para que pueda probar si algún objeto es iterable en una forma muy legible

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

Como lo harías con la función callable .

EDITAR: si tiene instalado Numpy, puede hacerlo simplemente: desde numpy import iterable , que es simplemente algo como

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

Si no tiene un número, simplemente puede implementar este código, o el anterior.


Aparte del intento regular y, excepto, podrías ejecutar la ayuda.

temp= [1,2,3,4] help(temp)

La ayuda brindaría todos los métodos que podrían ejecutarse en ese objeto (podría ser cualquier objeto y no ser una lista como en el ejemplo), que es temporal en este caso.

Nota: Esto sería algo que harías manualmente.


De acuerdo con el Glosario de Python 2 , los iterables son

todos los tipos de secuencia (como list , str y tuple ) y algunos tipos que no son de secuencia como dict y file y objetos de cualquier clase que defina con un __iter__() o __getitem__() . Los iterables se pueden usar en un bucle for y en muchos otros lugares donde se necesita una secuencia (zip (), map (), ...). Cuando un objeto iterable se pasa como un argumento a la función incorporada iter (), devuelve un iterador para el objeto.

Por supuesto, dado el estilo de codificación general para Python basado en el hecho de que es "Más fácil pedir perdón que permiso", la expectativa general es usar

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

Pero si necesita verificarlo explícitamente, puede probar un iterable por hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__") . __iter__ verificar ambos, porque las __iter__ no tienen un método __iter__ (al menos no en Python 2, en Python 3) y porque los objetos generator no tienen un método __getitem__ .


Desde Python 3.5 puede usar el módulo de typing de la biblioteca estándar para cosas relacionadas con el tipo:

from typing import Iterable ... if isinstance(my_item, Iterable): print(True)


En Python <= 2.5, no puedes y no debes, iterable era una interfaz "informal".

Pero desde Python 2.6 y 3.0 puede aprovechar la nueva infraestructura ABC (clase base abstracta) junto con algunos ABCs incorporados que están disponibles en el módulo de colecciones:

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)

Ahora, si esto es deseable o realmente funciona, es solo una cuestión de convenciones. Como puede ver, puede registrar un objeto no iterable como iterable, y generará una excepción en el tiempo de ejecución. Por lo tanto, isinstance adquiere un significado "nuevo": solo comprueba la compatibilidad de tipo "declarada", que es una buena forma de utilizar Python.

Por otro lado, si su objeto no satisface la interfaz que necesita, ¿qué va a hacer? Tomemos el siguiente ejemplo:

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 __name__ == "__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 el objeto no satisface lo que usted espera, simplemente lanza un TypeError, pero si se ha registrado el ABC correcto, su cheque es inútil. Por el contrario, si el método __iter__ está disponible, Python reconocerá automáticamente que el objeto de esa clase es iterable.

Entonces, si solo esperas un iterable, itéralo y olvídalo. Por otro lado, si necesita hacer cosas diferentes según el tipo de entrada, puede encontrar la infraestructura ABC bastante útil.


En lugar de verificar el atributo __iter__ , puede verificar el atributo __len__ , que es implementado por cada iterable Python integrado, incluidas las cadenas.

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

Los objetos no iterables no implementarían esto por razones obvias. Sin embargo, no detecta los iterables definidos por el usuario que no lo implementan, ni tampoco las expresiones generadoras, con las que iter puede lidiar. Sin embargo, esto se puede hacer en una línea, y la adición de una comprobación simple or expresión para los generadores solucionaría este problema. (Tenga en cuenta que el type(my_generator_expression) == generator escritura type(my_generator_expression) == generator generaría un NameError . Consulte this respuesta en su lugar).

Puedes usar GeneratorType desde tipos:

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

--- respuesta aceptada por utdemir

(Esto lo hace útil para verificar si puedes llamar a len en el objeto).


Esto no es suficiente: el objeto devuelto por __iter__ debe implementar el protocolo de iteración (es decir, el next método). Consulte la sección correspondiente en la documentation .

En Python, una buena práctica es "probar y ver" en lugar de "verificar".


He encontrado una buena solución here :

isiterable = lambda obj: isinstance(obj, basestring) / or getattr(obj, ''__iter__'', False)


La forma más fácil, respetando la tipificación del pato de Python, es detectar el error (Python sabe perfectamente qué espera de un objeto para convertirse en un iterador):

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())

Notas :

  1. Es irrelevante la distinción entre si el objeto no es iterable o __iter__ se implementó un buggy __iter__ , si el tipo de excepción es el mismo: de todos modos, no podrá iterar el objeto.
  2. Creo que entiendo su preocupación: ¿Cómo se puede callable si puedo confiar en la tipificación de pato para generar un __call__ AttributeError si __call__ no está definido para mi objeto, pero ese no es el caso para una verificación iterable?

    No sé la respuesta, pero puede implementar la función que di (y otros usuarios), o simplemente detectar la excepción en su código (su implementación en esa parte será como la función que escribí; solo asegúrese de aislar la Creación del iterador del resto del código para que pueda capturar la excepción y distinguirla de otro TypeError .


La función isiterable en el siguiente código devuelve True si el objeto es iterable. si no es iterable devuelve False

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

ejemplo

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


La mejor solución que he encontrado hasta ahora:

hasattr(obj, ''__contains__'')

que básicamente comprueba si el objeto implementa el operador in .

Ventajas (ninguna de las otras soluciones tiene las tres):

  • es una expresión (funciona como un lambda , a diferencia del intento ... excepto la variante)
  • está (debería) implementarse en todos los iterables, incluidas las cadenas (a diferencia de __iter__ )
  • Funciona en cualquier Python> = 2.5

Notas:

  • la filosofía de Python de "pedir perdón, no permiso" no funciona bien, por ejemplo, en una lista tiene iterables y no iterables y necesita tratar cada elemento de manera diferente según su tipo (trata los iterables en try y non). iterables en excepto funcionaría, pero se vería feo y engañoso)
  • las soluciones a este problema que intentan iterar realmente sobre el objeto (por ejemplo, [x para x en obj]) para comprobar si es iterable pueden generar importantes penalizaciones en el rendimiento para iterables grandes (especialmente si solo necesita los primeros elementos de iterable, por ejemplo) ejemplo) y debe evitarse

Me gustaría __iter__ un poco más la interacción de iter , __iter__ y __getitem__ y lo que sucede detrás de las cortinas. Con ese conocimiento, podrá comprender por qué lo mejor que puede hacer es

try: iter(maybe_iterable) print(''iteration will probably work'') except TypeError: print(''not iterable'')

Primero enumeraré los hechos y luego haré un recordatorio rápido de lo que sucede cuando emplea un bucle for en python, seguido de una discusión para ilustrar los hechos.

Hechos

  1. Puede obtener un iterador de cualquier objeto o llamando a iter(o) si al menos una de las siguientes condiciones se cumple:

    a) o tiene un método __iter__ que devuelve un objeto iterador. Un iterador es cualquier objeto con un __iter__ y __next__ (Python 2: next ).

    b) o tiene un método __getitem__

  2. Verificar una instancia de Iterable o Sequence , o verificar el atributo __iter__ no es suficiente.

  3. Si un objeto o implementa solo __getitem__ , pero no __iter__ , iter(o) construirá un iterador que intenta obtener elementos de o por índice entero, comenzando en el índice 0. El iterador detectará cualquier IndexError (pero no otros errores) que sea StopIteration y luego plantea StopIteration sí.

  4. En el sentido más general, no hay forma de comprobar si el iterador devuelto por iter es más que de prueba.

  5. Si un objeto o implementa __iter__ , la función iter se asegurará de que el objeto devuelto por __iter__ sea ​​un iterador. No hay comprobación de validez si un objeto solo implementa __getitem__ .

  6. __iter__ gana. Si un objeto o implementa tanto __iter__ como __getitem__ , iter(o) llamará __iter__ .

  7. Si desea que sus propios objetos sean iterables, siempre implemente el método __iter__ .

for bucles

Para seguir adelante, necesita comprender lo que sucede cuando emplea un bucle for en Python. Siéntase libre de pasar directamente a la siguiente sección si ya lo sabe.

Cuando se usa for item in o para algún objeto iterable, Python llama iter(o) y espera un objeto iterador como valor de retorno. Un iterador es cualquier objeto que implementa un __next__ (o next en Python 2) y un método __iter__ .

Por convención, el método __iter__ de un iterador debe devolver el objeto en sí (es decir, return self ). Python luego llama al next iterador hasta que se StopIteration . Todo esto sucede implícitamente, pero la siguiente demostración lo hace visible:

import random class DemoIterable(object): def __iter__(self): print(''__iter__ called'') return DemoIterator() class DemoIterator(object): def __iter__(self): return self def __next__(self): print(''__next__ called'') r = random.randint(1, 10) if r == 5: print(''raising StopIteration'') raise StopIteration return r

Iteración sobre 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

Discusión e ilustraciones.

En los puntos 1 y 2: obtener un iterador y cheques no confiables

Considere la siguiente clase:

class BasicIterable(object): def __getitem__(self, item): if item == 3: raise IndexError return item

Llamar a iter con una instancia de BasicIterable devolverá un iterador sin ningún problema porque BasicIterable implementa __getitem__ .

>>> b = BasicIterable() >>> iter(b) <iterator object at 0x7f1ab216e320>

Sin embargo, es importante tener en cuenta que b no tiene el atributo __iter__ y no se considera una instancia de Iterable o Sequence :

>>> from collections import Iterable, Sequence >>> hasattr(b, ''__iter__'') False >>> isinstance(b, Iterable) False >>> isinstance(b, Sequence) False

Esta es la razón por la cual Fluent Python de Luciano Ramalho recomienda llamar a iter y manejar el posible TypeError como la forma más precisa de verificar si un objeto es iterable. Citando directamente del libro:

A partir de Python 3.4, la forma más precisa de verificar si un objeto x es iterable es llamar a iter(x) y manejar una excepción TypeError si no lo es. Esto es más preciso que usar isinstance(x, abc.Iterable) , porque iter(x) también considera el método heredado __getitem__ , mientras que Iterable ABC no lo hace.

En el punto 3: Iterar sobre objetos que solo proporcionan __getitem__ , pero no __iter__

La iteración sobre una instancia de BasicIterable funciona como se esperaba: Python construye un iterador que intenta obtener elementos por índice, comenzando desde cero, hasta que se IndexError un IndexError . El método __getitem__ del objeto de demostración simplemente devuelve el item que fue suministrado como argumento a __getitem__(self, item) por el iterador devuelto por 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

Tenga en cuenta que el iterador genera StopIteration cuando no puede devolver el siguiente elemento y que el IndexError que se IndexError para el item == 3 se maneja internamente. Esta es la razón por la que el bucle sobre un BasicIterable con un bucle for funciona como se espera:

>>> for x in b: ... print(x) ... 0 1 2

Aquí hay otro ejemplo para llevar a casa el concepto de cómo el iterador devuelto por iter intenta acceder a los elementos por índice. WrappedDict no hereda de dict , lo que significa que las instancias no tendrán un método __iter__ .

class WrappedDict(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__ except KeyError: raise IndexError

Tenga en cuenta que las llamadas a __getitem__ se delegan a dict.__getitem__ para las cuales la notación de corchete es simplemente una forma abreviada.

>>> w = WrappedDict({-1: ''not printed'', ... 0: ''hi'', 1: '''', 2: ''!'', ... 4: ''not printed'', ... ''x'': ''not printed''}) >>> for x in w: ... print(x) ... hi !

En los puntos 4 y 5: iter busca un iterador cuando llama __iter__ :

Cuando se llama a iter(o) para un objeto o , iter se asegurará de que el valor de retorno de __iter__ , si el método está presente, sea un iterador. Esto significa que el objeto devuelto debe implementar __next__ (o next en Python 2) y __iter__ . iter no puede realizar comprobaciones de integridad para los objetos que solo proporcionan __getitem__ , porque no tiene forma de verificar si los elementos del objeto son accesibles por el índice entero.

class FailIterIterable(object): def __iter__(self): return object() # not an iterator class FailGetitemIterable(object): def __getitem__(self, item): raise Exception

Tenga en cuenta que la construcción de un iterador a partir de instancias FailIterIterable falla inmediatamente, mientras que la construcción de un iterador a partir de FailGetItemIterable tiene éxito, pero arrojará una excepción en la primera llamada a __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__ raise Exception Exception

En el punto 6: __iter__ gana

Este es sencillo. Si un objeto implementa __iter__ y __getitem__ , iter llamará __iter__ . Considera la siguiente clase

class IterWinsDemo(object): def __iter__(self): return iter([''__iter__'', ''wins'']) def __getitem__(self, item): return [''__getitem__'', ''wins''][item]

y la salida cuando se realiza un bucle en una instancia:

>>> iwd = IterWinsDemo() >>> for x in iwd: ... print(x) ... __iter__ wins

En el punto 7: sus clases iterables deberían implementar __iter__

Podría preguntarse por qué la mayoría de las secuencias __iter__ como list implementan un método __iter__ cuando __getitem__ sería suficiente.

class WrappedList(object): # note: no inheritance from list! def __init__(self, lst): self._list = lst def __getitem__(self, item): return self._list[item]

Después de todo, la iteración sobre las instancias de la clase anterior, que delega las llamadas a __getitem__ para list.__getitem__ (usando la notación entre corchetes), funcionará bien:

>>> wl = WrappedList([''A'', ''B'', ''C'']) >>> for x in wl: ... print(x) ... A B C

Las razones por las que sus iterables personalizados deben implementar __iter__ son las siguientes:

  1. Si implementa __iter__ , las instancias se considerarán como iterables y la isinstance(o, collections.Iterable) devolverá True .
  2. Si el objeto devuelto por __iter__ no es un iterador, iter fallará inmediatamente y generará un TypeError .
  3. El manejo especial de __getitem__ existe por razones de compatibilidad hacia atrás. Citando de nuevo desde Fluent Python:

Es por eso que cualquier secuencia de Python es iterable: todos implementan __getitem__ . De hecho, las secuencias estándar también implementan __iter__ , y las suyas también deberían hacerlo, porque el manejo especial de __getitem__ existe por razones de compatibilidad con versiones anteriores y puede desaparecer en el futuro (aunque no está en desuso cuando escribo esto).


Podrías probar esto:

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

Si podemos hacer un generador que se repita sobre él (pero nunca lo use para que no ocupe espacio), es iterable. Parece una especie de "duh". ¿Por qué necesitas determinar si una variable es iterable en primer lugar?


Siempre me ha eludido por qué python tiene que ser callable(obj) -> bool pero no iterable(obj) -> bool ...
seguramente es más fácil hacer hasattr(obj,''__call__'') incluso si es más lento.

Ya que casi todas las demás respuestas recomiendan el uso de try / except TypeError , donde la prueba de excepciones generalmente se considera una mala práctica en cualquier idioma, aquí hay una implementación de iterable(obj) -> bool me ha gustado más y que uso con frecuencia:

Para Python 2, usaré un lambda solo para aumentar el rendimiento ...
(en Python 3 no importa lo que uses para definir la función, def tiene aproximadamente la misma velocidad que lambda )

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

Tenga en cuenta que esta función se ejecuta más rápido para los objetos con __iter__ ya que no prueba para __getitem__ .

La mayoría de los objetos iterables deben depender de __iter__ donde los objetos de casos especiales __iter__ a __getitem__ , aunque cualquiera de los dos es necesario para que un objeto sea iterable.
(y dado que esto es estándar, también afecta a los objetos C)


pandas tiene una función incorporada como esa:

from pandas.util.testing import isiterable


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

Esto dirá sí a todo tipo de objetos iterables, pero dirá no a las cadenas en Python 2 . (Eso es lo que quiero, por ejemplo, cuando una función recursiva podría tomar una cadena o un contenedor de cadenas. En esa situación, pedir perdón puede llevar a obfuscode, y es mejor pedir permiso primero).

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)

Muchas otras estrategias aquí dirán sí a las cuerdas. Úsalos si eso es lo que quieres.

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__'')

Nota: is_iterable () dirá sí a cadenas de bytes de tipo y bytearray .

  • bytes objetos de bytes en Python 3 son iterables. True == is_iterable(b"string") == is_iterable("string".encode(''utf-8'')) No hay tal tipo en Python 2.
  • bytearray objetos bytearray en Python 2 y 3 son iterables True == is_iterable(bytearray(b"abc"))

El hasattr(x, ''__iter__'') OP hasattr(x, ''__iter__'') dirá sí a las cadenas en Python 3 y no en Python 2 (no importa si '''' o b'''' o u'''' ). Gracias a @LuisMasuelli por darse cuenta de que también te __iter__ un buggy __iter__ .


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

No ejecute los controles para ver si su pato es realmente un pato para ver si es iterable o no, trátelo como si fuera y reclame si no lo fue.