hasattr(obj, '__iter__') 与 collections

15 投票
5 回答
5080 浏览
提问于 2025-04-17 00:22

我看到有几个帖子推荐用 isinstance(obj, collections.Sequence) 来判断一个东西是不是列表,而不是用 hasattr(obj, '__iter__')

len(object) 或者 hasattr(object, __iter__)?

Python: 如何检查一个对象是否是序列

一开始我觉得很兴奋,因为检查一个对象是否有 __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__') 仍然是测试一个东西是否可迭代的最佳选择?

编辑:我只对检测内置类型感兴趣: dictlistset。(编辑:这真是傻 :))

编辑:我应该提到一下我为什么会关注这个问题。我有一个函数,它的参数可以是单个值或者一个序列。所以我想检测它是什么,如果是单个值就把它转换成序列,这样之后我就可以把它当作序列来处理。

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)

来源: Python: 如何检查一个对象是否是序列

但这需要定义这个函数,这让我不太想用它。

看起来 hasattr(arg, '__iter__') 仍然是最好的选择。

5 个回答

2

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

不过,你想到的解决方案更好——处理任何特殊情况(比如你的例子中的字符串),然后使用鸭子类型。这样,其他人就可以使用适合他们需求的任何容器,而确保它符合你代码要求的责任就落在他们身上。

6

collections.Iterable 可以用来判断一个对象是否可以被遍历(比如用 for x in obj 这样的方式),但是检查 __iter__ 方法并不能保证这一点。

字符串是可以遍历的数据类型,但在 Python 2.x 版本中,它并没有 __iter__ 这个方法。

1

在和一些同事讨论后,我得出结论,hasattr(arg, '__iter__') 这个写法虽然简洁,但并不是完美的,正如你们所提到的那样。最后我得到了这个结果:

if isinstance(arg, basestring):
    arg= set([arg])
else:
    try:
        selection= set(arg)
    except TypeError:
        selection= set([arg])

撰写回答