【问题标题】:hasattr(obj, '__iter__') vs collectionshasattr(obj, '__iter__') vs 集合
【发布时间】:2011-11-04 02:12:09
【问题描述】:

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

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

Python: check if an object is a sequence

起初我很兴奋,因为测试一个对象是否有__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。(编辑:这是愚蠢的 :) )

编辑:我应该包括让我研究这个的用例。我有一个函数,它接受一个可以是单个值或序列的 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: check if an object is a sequence

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

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

【问题讨论】:

  • 就个人而言,我检查isinstance( obj, basestring )(可迭代但通常不是您想要迭代的东西),如果不是,则对其进行迭代,捕获异常。

标签: python collections iterator


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我只对检测像dictlistset这样的内置类型感兴趣。我会将这个添加到我的问题中。
    【解决方案3】:

    collections.Sequence 很接近,但它对字符串返回 True。

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

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

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

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

    【讨论】:

      【解决方案4】:

      问题:我看到一些帖子推荐使用 isinstance(obj, collections.Sequence) 而不是 hasattr(obj, '__iter__') 来确定某物是否为列表。

      要确定某个东西是否是iterable,最可靠的方法是尝试创建一个iterator。这将确定一个对象是否实际上是可迭代的:

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

      一种稍微不太可靠但看起来干净的方法是针对abstract base class 进行测试。这将测试一个对象是否具有 __iter__() 方法,或者它是否注册为 Iterable(例如,str 的实例没有 __iter__()方法,但它被注册为可迭代):

      isinstance(obj, collections.Iterable)
      

      如果您对列表和类似列表的对象特别感兴趣,您可以针对 MutableSequence ABC 进行测试:

      isinstance(obj, collections.MutableSequence)
      

      也就是说,如果您依赖于具有特定类似列表行为的对象,则 Python 的方法是假设该方法存在,如果不存在则捕获异常。这称为duck typing

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

        • 检查basestring 是常见且有用的。另一个有用的检查是collections.Mapping(而不是dict),特别是因为您似乎对集合感兴趣,而Mapping 的键始终是集合。
        猜你喜欢
        • 1970-01-01
        • 2014-01-28
        • 1970-01-01
        • 2018-03-07
        • 1970-01-01
        • 2019-10-07
        • 1970-01-01
        • 2017-05-22
        • 2012-01-12
        相关资源
        最近更新 更多