关于Python中的迭代器和迭代的困惑

时间:2016-11-17 08:38:19

标签: python python-3.x iterator documentation iterable

我目前正在阅读Python 3.5的官方文档。

它声明range()是可迭代的,list()for是迭代器。 [section 4.3]

然而,here它表明zip()构成了一个迭代器。

我的问题是,当我们使用这条指令时:

list(zip(list1, list2))

我们使用迭代器(list())迭代另一个迭代器吗?

2 个答案:

答案 0 :(得分:3)

通过重复使用术语“迭代器”,文档在这里引起了一些混淆。

iterator protocol有三个组成部分:

  1. Iterables;你可以潜在地迭代并逐个获取其元素的东西。

  2. 迭代;做迭代的事情。每当您想要遍历迭代的所有项目时,您需要其中一项来跟踪您在过程中的位置。这些不可重复使用;一旦你到达终点,就是这样。对于大多数迭代,您可以创建多个独立迭代器,每个迭代迭代器独立跟踪位置。

  3. 迭代器的消费者;那些想要对这些物品做些什么的事情。

  4. for循环是后者的一个例子,所以#3。 for循环使用iter() function为您想要循环的任何内容生成迭代器(上面的#2),这样“无论什么”必须是可迭代的(#1)上文)。

    range()是#1的一个例子;它是可迭代的对象。您可以独立迭代多次:

    >>> r = range(5)
    >>> r_iter_1 = iter(r)
    >>> next(r_iter_1)
    0
    >>> next(r_iter_1)
    1
    >>> r_iter_2 = iter(r)
    >>> next(r_iter_2)
    0
    >>> next(r_iter_1)
    2
    

    这里r_iter_1r_iter_2是两个独立的迭代器,每当你要求下一个项目时,他们就会根据自己的内部簿记来这样做。

    list()的一个例子,它是可迭代的(#1)和迭代的消费者(#3)。如果将另一个iterable(#1)传递给list()调用,则会生成一个包含该iterable中所有元素的列表对象。但列表对象本身也是可迭代的。

    在Python 3中,

    zip()接受多个迭代(#1),它本身就是一个迭代器(#2)。 zip()为你给它的每个迭代存储一个新的迭代器(#2)。每次你向zip()询问下一个元素时,zip()都会使用每个包含的iterables中的下一个元素构建一个新元组:

    >>> lst1, lst2 = ['foo', 'bar'], [42, 81]
    >>> zipit = zip(lst1, lst2)
    >>> next(zipit)
    ('foo', 42)
    >>> next(zipit)
    ('bar', 81)
    

    所以最后,list(zip(list1, list2))同时使用list1list2作为迭代(#1),zip()消耗那些(#3)当它本身被消耗时通过外部list()电话。

答案 1 :(得分:1)

文档措辞严厉。以下是您所指的部分:

  

我们说这样的对象是 iterable ,也就是说,适合作为函数和构造的目标,这些函数和构造期望在供应耗尽之前可以获得连续项目的东西。我们已经看到for语句是一个迭代器。函数list()是另一个函数;它从迭代创建列表:

在本段中, iterator 不是指Python迭代器对象,而是“迭代某些东西”的一般概念。特别是,for语句不能是迭代器对象,因为它根本不是对象;它是一种语言结构。

回答您的具体问题:

  

......当我们使用这条指令时:

list(zip(list1, list2))
     

我们使用迭代器(list())迭代另一个迭代器吗?

不,list()不是迭代器。它是list类型的构造函数。它可以接受任何可迭代(包括迭代器)作为参数,并使用该iterable构造列表。

zip()是一个迭代器函数,也就是返回迭代器的函数。在您的示例中,它返回的迭代器传递给list(),它从中构造一个list对象。

判断对象是否为迭代器的一种简单方法是用它调用next(),看看会发生什么:

>>> list1 = [1, 2, 3]
>>> list2 = [4, 5, 6]

>>> zipped = zip(list1, list2)
>>> zipped
<zip object at 0x7f27d9899688>
>>> next(zipped)
(1, 4)

在这种情况下,将返回zipped的下一个元素。

>>> list3 = list(zipped)
>>> list3
[(2, 5), (3, 6)]

请注意,list3中只找到迭代器的最后两个元素,因为我们已经使用next()消耗了第一个元素。

>>> next(list3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator

这不起作用,因为列表不是迭代器。

>>> next(zipped)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

这一次,虽然zipped是一个迭代器,但用它调用next()会引发StopIteration,因为构建list3已经用尽了。