问题 hasattr(obj,'__ user__')vs collections


我看过几个帖子推荐 isinstance(obj, collections.Sequence) 代替 hasattr(obj, '__iter__') 确定某事是否是一个清单。

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

Python:检查对象是否是序列

起初我很兴奋,因为测试对象是否有 __iter__ 对我来说总是很脏。但经过进一步的审查,这似乎仍然是最好的解决方案,因为没有 isinstance 测试 collection 产生相同的结果。 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__') 如果某些东西是可迭代的,那么仍然是测试的最佳选择

编辑: 我只对检测内置类型感兴趣: dictlist,和 set编辑: 这很愚蠢:))

编辑: 我应该包括让我对此进行调查的用例。我有一个函数,它接受一个可以是单个值或序列的arg。所以我想检测它是什么,如果它是单个值就把它变成一个序列,所以我可以把它作为一个序列来处理。

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__') 仍然是最好的选择。


4120
2017-08-25 21:55


起源

stackoverflow.com/questions/1952464/... - Josh Lee
我亲自检查一下 isinstance( obj, basestring ) (可迭代但通常不想迭代的东西)如果没有,则迭代它,捕获异常。 - Russell Borogove


答案:


collections.Iterable 将保证对象是可迭代的(如使用 for x in obj)但检查 __iter__ 将不会。

字符串是可迭代的数据类型,但在Python2.x上它没有 __iter__ 方法。


5
2017-08-25 22:16





你也可以添加 __iter__ 到你自己的班级,所以任何测试都没有 hasattr(obj, '__iter__') 不能保证检测到那些。您没有在问题中指明您只关心内置集合。


2
2017-08-25 21:58



我只对检测内置类型感兴趣 dict, list,和 set。我会将此添加到我的问题中。 - keegan3d


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

但是,您提出的解决方案更好 - 处理任何特殊情况(例如字符串,在您的情况下),然后使用duck typing。然后,这允许其他人使用适合他们需要的任何容器,并负责确保它符合您的代码所需的内容。


2
2017-08-26 23:15





在与一些同事交谈后,我得出结论 hasattr(arg, '__iter__')你可能也指出,它可能是一个不错的单线,但它远非完美。这就是我最终的结果:

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

1
2017-08-26 22:15



检查 basestring 是常见且有用的。另一个有用的检查是 collections.Mapping (而不是 dict),特别是因为你似乎对集合感兴趣,而且关键是 Mapping 总是一套。 - hobs


问题:我已经看到一些帖子推荐isinstance(obj,collections.Sequence)而不是hasattr(obj,'__ user__')以确定某些内容是否为列表。

确定是否有事 迭代,最可靠的方法是尝试创建一个 迭代器。这将决定一个对象是否实际可迭代:

def is_iterable(obj):
    try:
        iter(obj)
    except TypeError:
        return False
    else:
        return True

稍微不那么可靠,但看起来干净的方法是测试 抽象基类。这将测试对象是否具有__iter __()方法或是否已注册为 可迭代 (例如,一个实例 海峡 没有__iter __()方法,但它被注册为可迭代的):

isinstance(obj, collections.Iterable)

如果你对列表和类似列表的对象特别感兴趣,你可以测试一下 MutableSequence ABC:

isinstance(obj, collections.MutableSequence)

也就是说,如果你依赖的是一个具有特定列表行为的对象,那么Pythonic方法就是假设该方法存在并且如果不存在则捕获异常。这就是所谓的 鸭子打字


1
2018-06-01 05:08