I've seen a couple posts recommending isinstance(obj, collections.Sequence)
instead of hasattr(obj, '__iter__')
to determine if something is a list.
len(object) or hasattr(object, __iter__)?
Python: check if an object is a sequence
At first I was excited because testing if an object has __iter__
always seemed dirty to me. But after further review this still seems to be the best solution because none of the isinstance
tests on collection
yield the same results. collections.Sequence
is close but it returns True
for strings.
hasattr(obj, '__iter__')
set([]): True
{}: True
[]: True
'str': False
1: False
isinstance(obj, collections.Iterable)
set([]): True
{}: True
[]: True
'str': True
1: False
isinstance(obj, collections.Iterator)
set([]): False
{}: False
[]: False
'str': False
1: False
isinstance(obj, collections.Sequence)
set([]): False
{}: False
[]: True
'str': True
1: False
Here is the code I used to generate this:
import collections
testObjs = [
set(),
dict(),
list(),
'str',
1
]
print "hasattr(obj, '__iter__')"
for obj in testObjs:
print ' %r: %r' % (obj, hasattr(obj, '__iter__'))
print
print "isinstance(obj, collections.Iterable)"
for obj in testObjs:
print ' %r: %r' % (obj, isinstance(obj, collections.Iterable))
print
print "isinstance(obj, collections.Iterator)"
for obj in testObjs:
print ' %r: %r' % (obj, isinstance(obj, collections.Iterator))
print
print "isinstance(obj, collections.Sequence)"
for obj in testObjs:
print ' %r: %r' % (obj, isinstance(obj, collections.Sequence))
print
Am I missing something or is hasattr(obj, '__iter__')
still the best option for testing if something is iterable?
EDIT: I am only interested in detecting the builtin types: dict
, list
, and set
.(EDIT: this is foolish :))
EDIT: I should have included the use case that got me looking into this. I have a function that takes an arg that can be a single value or a sequence. So I want to detect what it is and turn it into a sequence if it's a single value so I can deal with it as a sequence after that.
if hasattr(arg, '__iter__'):
arg= set(arg)
else:
arg= set([arg])
One solution to this is just to let it throw an exception if the object cannot be iterated. But that doesn't work in my use case. Another solution is to use something like:
import collections
def issequenceforme(obj):
if isinstance(obj, basestring):
return False
return isinstance(obj, collections.Sequence)
From: Python: check if an object is a sequence
But this requires this function to be defined which makes me not want to use it.
It looks like hasattr(arg, '__iter__')
is still the best option.