how to tell a variable is iterable but not a string
Use isinstance (I don't see why it's bad practice)
import types
if not isinstance(arg, types.StringTypes):
Note the use of StringTypes. It ensures that we don't forget about some obscure type of string.
On the upside, this also works for derived string classes.
class MyString(str):
pass
isinstance(MyString(" "), types.StringTypes) # true
Also, you might want to have a look at this previous question.
Cheers.
NB: behavior changed in Python 3 as StringTypes
and basestring
are no longer defined. Depending on your needs, you can replace them in isinstance
by str
, or a subset tuple of (str, bytes, unicode)
, e.g. for Cython users.
As @Theron Luhn mentionned, you can also use six
.
As of 2017, here is a portable solution that works with all versions of Python:
#!/usr/bin/env python
import collections
import six
def iterable(arg):
return (
isinstance(arg, collections.Iterable)
and not isinstance(arg, six.string_types)
)
# non-string iterables
assert iterable(("f", "f")) # tuple
assert iterable(["f", "f"]) # list
assert iterable(iter("ff")) # iterator
assert iterable(range(44)) # generator
assert iterable(b"ff") # bytes (Python 2 calls this a string)
# strings or non-iterables
assert not iterable(u"ff") # string
assert not iterable(44) # integer
assert not iterable(iterable) # function
Since Python 2.6, with the introduction of abstract base classes, isinstance
(used on ABCs, not concrete classes) is now considered perfectly acceptable. Specifically:
from abc import ABCMeta, abstractmethod
class NonStringIterable:
__metaclass__ = ABCMeta
@abstractmethod
def __iter__(self):
while False:
yield None
@classmethod
def __subclasshook__(cls, C):
if cls is NonStringIterable:
if any("__iter__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
This is an exact copy (changing only the class name) of Iterable
as defined in _abcoll.py
(an implementation detail of collections.py
)... the reason this works as you wish, while collections.Iterable
doesn't, is that the latter goes the extra mile to ensure strings are considered iterable, by calling Iterable.register(str)
explicitly just after this class
statement.
Of course it's easy to augment __subclasshook__
by returning False
before the any
call for other classes you want to specifically exclude from your definition.
In any case, after you have imported this new module as myiter
, isinstance('ciao', myiter.NonStringIterable)
will be False
, and isinstance([1,2,3], myiter.NonStringIterable)
will be True
, just as you request -- and in Python 2.6 and later this is considered the proper way to embody such checks... define an abstract base class and check isinstance
on it.