迭代器是否也是可迭代对象?

21

我发现:

>>> a={'x':42, 'y':3.14, 'z':7}
>>> b=a.__iter__()
>>> b.__dir__()
['__next__', ..., '__iter__', ...]
>>> b
<set_iterator object at 0x7efdd4e5afc0>

迭代器是否总是有__iter__方法?

根据https://dev59.com/32kw5IYBdhLWcg3wdKMv#9884259 的解释,迭代器也是可迭代的。那么迭代器是否总是拥有__iter__方法呢?


1
是的,它总是这样做。而且它应该返回 self - juanpa.arrivillaga
不过说真的,看看这个gist吧。我认为它应该能够澄清一些关于for循环、迭代器和可迭代对象的困惑。 - juanpa.arrivillaga
我认为值得注意的是,有时人们关于迭代器和可迭代对象的语言与实际协议细节相反。也就是说,如果有人说“X是可迭代的”,他们可能意图排除迭代器(尽管迭代器始终具有__iter__方法,因此在官方意义上是可迭代的)。没有一个好的单词来表示“非迭代器可迭代对象”,因此一些人只是使用“可迭代对象”来表示,即使这并不准确。我在之前的回答中对这个话题进行了详细阐述。 - Blckknght
4个回答

18

可迭代对象需要实现__iter__方法或__getitem__方法:

如果一个对象实现了__iter__()__getitem__(),那么它就可以被用于for循环。

迭代器需要一个__iter__方法(返回self)和一个__next__方法(关于__next__我不是100%确定)。

迭代器总是有__iter__方法吗?

是的!

这也在数据模型中有说明:

object.__iter__(self)

当容器需要一个迭代器时,会调用此方法。此方法应返回一个新的迭代器对象,该对象可以迭代容器中的所有对象。对于映射,它应迭代容器的键。

迭代器对象也需要实现此方法;它们需要返回自己。有关迭代器对象的更多信息,请参见迭代器类型。

(强调是我的)

至于您的第二个问题:

迭代器也是可迭代对象吗?

是的,因为它有一个__iter__方法。

附加说明

除了正式的实现,还可以通过检查是否可以调用iter()来轻松检查某个对象是否可迭代:

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

同样,可以通过检查在某些东西上调用iter()是否返回其本身来检查是否为迭代器:
def is_iterator(something):
    try:
        return iter(something) is something  # it needs to return itself to be an iterator
    except TypeError:
        return False

但是不要在开发代码中使用它们,这些只是用于“可视化”。大多数情况下,您只需使用for ... in ...迭代某个东西,或者如果您需要一个迭代器,则使用iterator = iter(...),然后通过调用next(iterator)来处理迭代器,直到它抛出StopIteration


谢谢。我可以在哪里找到“可迭代对象具有__iter__方法或__getitem__和__len__方法”的参考资料?哪些可迭代对象没有__iter__方法? - user3284469
@Ben 我添加了一些链接 :) - MSeifert
谢谢。有哪些内置的可迭代对象没有__iter__方法? - user3284469
@Ben 有具体的使用场景吗?通常情况下,你不需要经常使用迭代器,只需遍历你所拥有的内容,然后查看它是否有效即可。 - MSeifert
3
在Python 2中,str没有__iter__属性。在Python 3中,我不知道有哪些内置的可迭代类型没有__iter__属性。 - user2357112
显示剩余3条评论

7

迭代器是可迭代的。是的,一个迭代器总是有一个__iter__方法。

在迭代器上调用iter,即调用__iter__钩子,会返回相同的迭代器:

>>> it = iter([]) # return iterator from iterable
>>> it is iter(iter(it)) is it.__iter__().__iter__().__iter__()
True

一个典型的方法链示例

你也一定注意到了,大多数自定义类的迭代器协议实现总是遵循以下方式:

def __iter__(self):
    return self

如果迭代没有通过return iter(...)委派给另一个迭代器,那么它就不会被委派。

如果一个迭代器没有实现迭代器协议,那么这将是相当反直觉的,你认为呢?协议中的__iter__实现如下:

iterator.__iter__()

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements.

[重点标注为我的]

这种行为与内置函数返回的迭代器对象一致:

>>> m = map(None, [])
>>> m
<map object at 0x...>
>>> m is m.__iter__().__iter__().__iter__()
True

对于反复使用双下划线,我向您道歉,这使人们认为这是正确的做法。但事实并非如此。 详情请见


不是这样的。迭代器可以有__getitem__而不是__iter__,因此这并不正确。 - RobertB
2
不,你想的是“可迭代对象”。而“迭代器”总是有一个__iter__方法。 - MSeifert
这是必需的,以便容器和迭代器都可以与for和in语句一起使用。那似乎是文档错误。一个容器只需要实现__contains__,然后__iter__就不是必需的,它就可以在in语句的右侧使用了。 - wim
@wim 我认为他们指的是for循环语法:for ... in ...,其中包括“for”和“in”。实际上并不是像contains中的in - MSeifert
我对此持怀疑态度。在语法中,循环只是被称为“for语句”。而该文本是从16年前的(https://github.com/python/cpython/commit/93656e76f9d7f11af13f5dc0ccf7b2051033fa29)(!) , 因此意图可能是通过迭代描述旧式成员资格测试。否则,没有充分的理由明确地将“for”和“in”语句写为两个不同的语句。 - wim
此外,问题部分中的in语句实际上与成员测试操作相关联。 - wim

1

迭代器是可迭代的。

这在这里有记录和解释:

迭代器需要有一个返回迭代器对象本身的__iter__()方法,因此每个迭代器也都是可迭代的

可迭代的不一定是迭代器

根据定义,迭代器必须有一个__next__方法。举个简单的反例:

>>> ''.__next__
AttributeError: 'str' object has no attribute '__next__'

一个字符串对象是可迭代的,但不是迭代器。

谢谢。"字符串对象是可迭代的,但不是迭代器。" 但是'abc'.__iter__存在且'abc'是一个字符串对象。我有什么遗漏吗? - user3284469
@MSeifert string_iterator对象不是迭代器吗? - user3284469
string_iterator 是一个迭代器,但 str 不是(str 只是可迭代的)。 - MSeifert
2
@Ben 因为要成为迭代器,需要一个 __next__ 方法。 - juanpa.arrivillaga
2
@juanpa.arrivillaga 不,它们确实是类。只需尝试:map.__iter__map.__next__。我并不是说openiter是迭代器。这两个都是真正返回迭代器的工厂函数。 - MSeifert
显示剩余9条评论

0

迭代器是否总是具有__iter__方法?

是的。所有迭代器都有一个__iter__方法,该方法返回自身。来自docs

iterator.__iter__()

返回迭代器对象本身。这是为了允许容器和迭代器与for和in语句一起使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接