hasattr(obj, '__iter__') 与 collections
我看到有几个帖子推荐用 isinstance(obj, collections.Sequence)
来判断一个东西是不是列表,而不是用 hasattr(obj, '__iter__')
。
len(object) 或者 hasattr(object, __iter__)?
一开始我觉得很兴奋,因为检查一个对象是否有 __iter__
这个属性总让我觉得不太干净。但经过进一步的审查,我发现这仍然是最好的解决方案,因为在 collection
上的 isinstance
测试结果并不一致。虽然 collections.Sequence
也很接近,但它对字符串返回 True
。
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
这是我用来生成这个的代码:
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
我是不是漏掉了什么,还是说 hasattr(obj, '__iter__')
仍然是测试一个东西是否可迭代的最佳选择?
编辑:我只对检测内置类型感兴趣: dict
、 list
和 set
。(编辑:这真是傻 :))
编辑:我应该提到一下我为什么会关注这个问题。我有一个函数,它的参数可以是单个值或者一个序列。所以我想检测它是什么,如果是单个值就把它转换成序列,这样之后我就可以把它当作序列来处理。
if hasattr(arg, '__iter__'):
arg= set(arg)
else:
arg= set([arg])
解决这个问题的一种方法是让它抛出异常,如果对象不能被迭代。但这在我的使用场景中不适用。另一种解决方案是使用类似的东西:
import collections
def issequenceforme(obj):
if isinstance(obj, basestring):
return False
return isinstance(obj, collections.Sequence)
但这需要定义这个函数,这让我不太想用它。
看起来 hasattr(arg, '__iter__')
仍然是最好的选择。
5 个回答
collections.Sequence
这个东西差不多,但它对字符串返回的是 True。
collections.Iterable
是用来表示可迭代对象的,也就是说,它是一种容器,可以让你一个一个地遍历里面的东西。字符串也算在内,因为你可以一个一个地遍历字符串中的每个字符。collections.Sequence
是可迭代对象的一个子集,它保证了遍历的顺序。比如列表和字符串在遍历时总是按照相同的顺序返回True
,而集合和字典则返回False
,因为你不知道里面的东西会以什么顺序出来。
如果你确定只想检测内置类型(这通常被认为是一种可疑的做法,大家更喜欢用 鸭子类型),那么你可以这样做:
if isinstance(arg, (set, list, dict, tuple)):
print "I got a built-in iterable."
else:
print "Hey, this ain't iterable!"
不过,你想到的解决方案更好——处理任何特殊情况(比如你的例子中的字符串),然后使用鸭子类型。这样,其他人就可以使用适合他们需求的任何容器,而确保它符合你代码要求的责任就落在他们身上。
collections.Iterable
可以用来判断一个对象是否可以被遍历(比如用 for x in obj
这样的方式),但是检查 __iter__
方法并不能保证这一点。
字符串是可以遍历的数据类型,但在 Python 2.x 版本中,它并没有 __iter__
这个方法。
在和一些同事讨论后,我得出结论,hasattr(arg, '__iter__')
这个写法虽然简洁,但并不是完美的,正如你们所提到的那样。最后我得到了这个结果:
if isinstance(arg, basestring):
arg= set([arg])
else:
try:
selection= set(arg)
except TypeError:
selection= set([arg])