Is there a method like isiterable
? The only solution I have found so far is to call
hasattr(myObj, \'__iter__\')
But I am not
I often find convenient, inside my scripts, to define an iterable
function.
(Now incorporates Alfe's suggested simplification):
import collections
def iterable(obj):
return isinstance(obj, collections.Iterable):
so you can test if any object is iterable in the very readable form
if iterable(obj):
# act on iterable
else:
# not iterable
as you would do with thecallable
function
EDIT: if you have numpy installed, you can simply do: from numpy import iterable
,
which is simply something like
def iterable(obj):
try: iter(obj)
except: return False
return True
If you do not have numpy, you can simply implement this code, or the one above.
try:
iterator = iter(theElement)
except TypeError:
# not iterable
else:
# iterable
# for obj in iterator:
# pass
Use the Abstract Base Classes. They need at least Python 2.6 and work only for new-style classes.
from collections.abc import Iterable # import directly from collections for Python < 3.3
if isinstance(theElement, Iterable):
# iterable
else:
# not iterable
However, iter()
is a bit more reliable as described by the documentation:
Checking
isinstance(obj, Iterable)
detects classes that are registered as Iterable or that have an__iter__()
method, but it does not detect classes that iterate with the__getitem__()
method. The only reliable way to determine whether an object is iterable is to calliter(obj)
.
This isn't sufficient: the object returned by __iter__
must implement the iteration protocol (i.e. next
method). See the relevant section in the documentation.
In Python, a good practice is to "try and see" instead of "checking".
Instead of checking for the __iter__
attribute, you could check for the __len__
attribute, which is implemented by every python builtin iterable, including strings.
>>> 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
None-iterable objects would not implement this for obvious reasons. However, it does not catch user-defined iterables that do not implement it, nor do generator expressions, which iter
can deal with. However, this can be done in a line, and adding a simple or
expression checking for generators would fix this problem. (Note that writing type(my_generator_expression) == generator
would throw a NameError
. Refer to this answer instead.)
You can use GeneratorType from types:
>>> import types >>> types.GeneratorType <class 'generator'> >>> gen = (i for i in range(10)) >>> isinstance(gen, types.GeneratorType) True
--- accepted answer by utdemir
(This makes it useful for checking if you can call len
on the object though.)
The easiest way, respecting the Python's duck typing, is to catch the error (Python knows perfectly what does it expect from an object to become an iterator):
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())
Notes:
__iter__
has been implemented, if the exception type is the same: anyway you will not be able to iterate the object.I think I understand your concern: How does callable
exists as a check if I could also rely on duck typing to raise an AttributeError
if __call__
is not defined for my object, but that's not the case for iterable checking?
I don't know the answer, but you can either implement the function I (and other users) gave, or just catch the exception in your code (your implementation in that part will be like the function I wrote - just ensure you isolate the iterator creation from the rest of the code so you can capture the exception and distinguish it from another TypeError
.
Not really "correct" but can serve as quick check of most common types like strings, tuples, floats, etc...
>>> '__iter__' in dir('sds')
True
>>> '__iter__' in dir(56)
False
>>> '__iter__' in dir([5,6,9,8])
True
>>> '__iter__' in dir({'jh':'ff'})
True
>>> '__iter__' in dir({'jh'})
True
>>> '__iter__' in dir(56.9865)
False