如何检查一个对象是否是Python中的迭代器?

55 投票
10 回答
26895 浏览
提问于 2025-04-15 23:51

我可以检查一下有没有一个 next() 方法,但这样就够了吗?有没有更合适的做法呢?

10 个回答

10

要成为一个迭代器,一个对象必须通过三个测试:

  • obj 必须有一个 __iter__ 方法
  • obj 必须有一个 next 方法(在 Python 3 中是 __next__
  • obj.__iter__() 必须返回 obj 本身

所以,自己测试的代码大概是这样的:

def is_iterator(obj):
    if (
            hasattr(obj, '__iter__') and
            hasattr(obj, 'next') and      # or __next__ in Python 3
            callable(obj.__iter__) and
            obj.__iter__() is obj
        ):
        return True
    else:
        return False
19

一个对象如果能被循环遍历,就叫做可迭代对象。
你可以通过检查这个对象是否有__iter__()这个方法来判断它是否可迭代。

hasattr(object,'__iter__')

在Python 2.x中,这种方法会漏掉一些字符串对象和其他内置的序列类型,比如unicode、xrange和buffer。但在Python 3中,这种方法是有效的。

另一种方法是使用iter方法来测试:

try:
   iter(object)
except TypeError:
   #not iterable
68

在Python 2.6或更新的版本中,进行这种行为检查的一个常用方法是使用标准库中collections模块的抽象基类进行“成员检查”。

>>> import collections
>>> isinstance('ciao', collections.Iterable)
True
>>> isinstance(23, collections.Iterable)
False
>>> isinstance(xrange(23), collections.Iterable)
True

实际上,这种检查的主要设计原因就是为了引入新的抽象基类(还有一个重要原因是提供“混入功能”,这就是为什么它们是抽象基类而不仅仅是接口——不过这不适用于collections.Iterable,它的存在是为了允许使用isinstanceissubclass进行这样的检查)。抽象基类允许那些并不直接继承它们的类也能被“注册”为子类,这样这些类就可以在进行检查时被视为抽象基类的“子类”;而且,它们可以内部执行所有需要的特殊方法检查(在这个例子中是__iter__),所以你不需要自己去做。

如果你还在使用旧版本的Python,“与其请求许可,不如事后求情”:

def isiterable(x):
  try: iter(x)
  except TypeError: return False
  else: return True

但这种方法没有新方法那么快速和简洁。

注意,对于这个特殊情况,你通常会想要特别处理字符串(字符串是可迭代的,但在大多数应用场景中希望将其视为“标量”)。无论你使用什么方法来检查是否可迭代,如果需要这种特殊处理,只需在前面加一个isinstance(x, basestring)的检查——例如:

def reallyiterable(x):
  return not isinstance(x, basestring) and isinstance(x, collections.Iterable)

编辑:正如评论中指出的,问题关注的是一个对象是否是迭代器,而不是它是否可迭代(所有迭代器都是可迭代的,但反之不成立——并不是所有可迭代的对象都是迭代器)。isinstance(x, collections.Iterator)是检查这个条件的完美方法。

撰写回答