关于python:hasattr(obj,’__ user__’)vs collections

hasattr(obj, '__iter__') vs collections

我看到一些帖子建议用isinstance(obj, collections.Sequence)而不是hasattr(obj, '__iter__')来确定某个东西是否是一个列表。

len(object)或hasattr(object,uu iter_uuuuuuu)?

python:检查对象是否是序列

起初我很兴奋,因为测试对象是否有__iter__对我来说总是很脏。但经过进一步的审查,这似乎仍然是最佳的解决方案,因为在collection上的isinstance测试没有产生相同的结果。collections.Sequence已关闭,但它返回True作为字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

下面是我用来生成这个的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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(编辑:这太愚蠢了:)

编辑:我应该包括让我研究这个的用例。我有一个函数接受一个可以是单个值或序列的参数。所以我想检测它是什么,把它变成一个序列,如果它是一个单一的值,那么我可以把它作为一个序列来处理。

1
2
3
4
if hasattr(arg, '__iter__'):
    arg= set(arg)
else:
    arg= set([arg])

解决这个问题的一个方法是,如果对象不能被迭代,就让它抛出一个异常。但这在我的用例中不起作用。另一种解决方案是使用如下内容:

1
2
3
4
5
6
import collections

def issequenceforme(obj):
    if isinstance(obj, basestring):
        return False
    return isinstance(obj, collections.Sequence)

发件人:python:检查对象是否为序列

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

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


collections.Iterable将保证对象是可访问的或不可访问的(如使用for x in obj,但检查__iter__将不可访问。

字符串是一个可Iterable数据类型,但在python2.x上它没有__iter__方法。


collections.Sequence已关闭,但字符串返回true。

  • collections.Iterable是一个不可迭代的对象,也就是说,某种形式的容器允许您逐个迭代它所包含的对象。字符串包含在其中,因为可以对每个字符进行迭代是一个字符串。
  • collections.Sequence是一个子集的不可重复的对象,它保证了迭代的顺序。列表和字符串将返回True,因为同一列表或字符串的迭代顺序始终相同,而集合和字典将返回False,因为您不知道内部对象将以什么顺序出现。

如果您确定只想检测内置类型(通常被认为是可疑的做法,首选duck类型),那么可以这样做:

1
2
3
4
if isinstance(arg, (set, list, dict, tuple)):
    print"I got a built-in iterable."
else:
    print"Hey, this ain't iterable!"

然而,您提出的解决方案是更好的-处理任何特殊情况(如字符串,在您的情况下),然后使用duck类型。这样,其他人就可以使用适合他们需要的容器,并有责任确保它符合您的代码对他们的要求。


您还可以将__iter__添加到自己的类中,因此任何缺少hasattr(obj, '__iter__')的测试都不能保证检测到这些测试。您没有在问题中指定只关心内置集合。


Question: I've seen a couple posts recommending isinstance(obj, collections.Sequence) instead of hasattr(obj, '__iter__') to determine if something is a list.

为了确定某个东西是否是可迭代的,最可靠的方法是尝试创建一个迭代器。这将决定一个对象是否真的是可重写的:

1
2
3
4
5
6
7
def is_iterable(obj):
    try:
        iter(obj)
    except TypeError:
        return False
    else:
        return True

一种稍不可靠但看起来整洁的方法是对抽象基类进行测试。这将测试对象是否具有"iter"方法或是否注册为"iterable"(例如,str的实例没有"iter"方法,但注册为"iterable"):

1
isinstance(obj, collections.Iterable)

如果您对列表和类似于列表的对象特别感兴趣,那么可以针对可变顺序ABC进行测试:

1
isinstance(obj, collections.MutableSequence)

也就是说,如果您依赖的对象具有特定的类似列表的行为,则pythonic方法是假定该方法存在,如果不存在,则捕获异常。这叫鸭子打字。


在和一些同事交谈之后,我得出结论,hasattr(arg, '__iter__')可能是一个很好的单行程序,但它远不是完美的,正如你们所指出的。这就是我最后得出的结论:

1
2
3
4
5
6
7
if isinstance(arg, basestring):
    arg= set([arg])
else:
    try:
        selection= set(arg)
    except TypeError:
        selection= set([arg])