迭代器,可迭代和迭代到底是什么?


442

Python中“可迭代”,“迭代器”和“迭代”的最基本定义是什么?

我已经阅读了多个定义,但是我无法确定确切的含义,因为它仍然不会陷入。

有人可以在外行方面为我提供3个定义的帮助吗?

Answers:


529

迭代是一个总称,表示一件一件一件一件一件接一件的物品。每当您使用循环(显式或隐式)遍历一组项目时,即迭代。

在Python中,iterableiterator具有特定的含义。

一个迭代是具有对象__iter__返回一个方法迭代,或者其限定__getitem__,可以采取顺序索引从零启动方法(并发出IndexError时,索引不再有效)。因此,可迭代对象是可以从中获取迭代器的对象。

一个迭代器是具有一个对象next(Python的2)或__next__(Python 3的)方法。

每当在Python中使用for循环或map或列表理解等时,next都会自动调用该方法以从迭代器获取每个项,从而进行迭代过程。

一个开始学习的好地方是本教程迭代器部分和标准类型页面迭代器类型部分。了解基础知识之后,请尝试“功能编程HOWTO”的“ 迭代器”部分


1
注意collections.abc.AsyncIterator测试__aiter____anext__方法。这是3.6中的新增功能。
Janus Troelsen

1
@jlh为什么__len__一定要与迭代联系在一起?知道某物的长度如何帮助您进行迭代?
shadowtalker

2
@shadowtalker,这将有助于知道哪些索引有效,因此您可以知道可以使用哪些索引__getitem__
jlh

4
@jlh听起来您正在提出非常自觉的dfeault行为。考虑{'a': 'hi', 'b': 'bye'}具有2长度,但不能被0,1,或2被索引
shadowtalker

2
@shadowtalker。但是字典有一种__iter__方法。我认为jlh所指的是可迭代的对象,因为它们定义了:“一种__getitem__可以从零开始的顺序索引的方法”。
丰富,

337

这是我在教授Python类时使用的解释:

一个ITERABLE是:

  • 任何可以循环播放的内容(例如,您可以循环播放字符串或文件)或
  • 任何可能出现在for循环右侧的内容: for x in iterable: ...
  • 您可以呼叫的任何内容iter()都会传回ITERATOR: iter(obj)
  • 一个定义的对象,该对象__iter__返回一个新鲜的ITERATOR,或者它可能具有__getitem__适合于索引查找的方法。

ITERATOR是一个对象:

  • 状态会记住迭代过程中的位置,
  • 使用以下__next__方法:
    • 返回迭代中的下一个值
    • 更新状态以指向下一个值
    • 通过提高发出信号 StopIteration
  • 并且这是可自我迭代的(意味着它具有__iter__返回的方法self)。

笔记:

  • __next__Python 3中的方法是Python 2中的拼写next,并且
  • 内置函数next()在传递给它的对象上调用该方法。

例如:

>>> s = 'cat'      # s is an ITERABLE
                   # s is a str object that is immutable
                   # s has no state
                   # s has a __getitem__() method 

>>> t = iter(s)    # t is an ITERATOR
                   # t has state (it starts by pointing at the "c"
                   # t has a next() method and an __iter__() method

>>> next(t)        # the next() function returns the next value and advances the state
'c'
>>> next(t)        # the next() function returns the next value and advances
'a'
>>> next(t)        # the next() function returns the next value and advances
't'
>>> next(t)        # next() raises StopIteration to signal that iteration is complete
Traceback (most recent call last):
...
StopIteration

>>> iter(t) is t   # the iterator is self-iterable

新鲜迭代器是什么意思?
lmiguelvargasf '02

13
@lmiguelvargasf将“新鲜”表示为“新的和未消费”,而不是“用尽或部分消耗”。这个想法是一个新的迭代器从头开始,而部分使用的迭代器则从中断的地方开始。
Raymond Hettinger

根据特定的python构造或内置函数或方法调用,第二,第三和第四项目符号清楚地表明了您的意思。但是第一个项目符号(“任何可以循环的内容”)都没有那么清楚。同样,第一个项目符号似乎与第二个项目符号重叠,因为第二个项目符号是关于for循环的,而第一个项目符号是关于“循环”的。您能解决这些吗?
水源

2
请考虑重新措辞“任何事情你可以叫iter()为”什么,你可以通到“ iter()
水源

98

上面的答案很棒,但是正如我所见到的大多数一样,对于像我这样的人来说,不要强调这种区别

同样,人们倾向于通过在__foo__()前面放置诸如“ X是具有方法的对象”之类的定义来获得“ Python风格” 。这样的定义是正确的-它们基于鸭子式的哲学,但是当试图以简单的方式理解概念时,对方法的关注往往会介于两者之间。

因此,我添加了我的版本。


用自然语言

  • 迭代是在一行元素中一次获取一个元素的过程。

在Python中,

  • Iterable是一个很好的可迭代对象,简单地说,意味着可以在迭代中使用它,例如使用for循环。怎么样?通过使用迭代器。我会在下面解释。

  • ...,而迭代器是一个对象,它定义了如何实际执行迭代-特别是下一个元素是什么。这就是为什么它必须有next()方法的原因 。

迭代器本身也是可迭代的,区别在于它们的__iter__()方法返回相同的object(self),而不管其先前的调用是否已消耗其项目next()


那么,Python解释器看到for x in obj:语句时会怎么想?

看,for循环。看起来像是一个迭代器的工作...让我们得到一个。...有obj一个人,让我们问他。

“先生obj,您有迭代器吗?” (...调用iter(obj),这些调用 obj.__iter__()愉快地发出了一个闪亮的新迭代器_i。)

好的,那很简单...让我们开始迭代。(x = _i.next()... x = _i.next()...)

由于Mr. Mr obj成功地通过了某种测试(通过某种方法返回有效的迭代器),因此我们用形容词来奖励他:您现在可以称他为“ Iterable Mr. obj”。

但是,在简单的情况下,通常不会从分别拥有Iterator和Iterable中受益。因此,您定义一个对象,这也是它自己的迭代器。(Python并不真正在乎_i发出的obj不是那么闪亮,而仅仅是obj它本身。)

这就是为什么在我见过的大多数示例中(以及一遍又一遍使我困惑的原因)中,您可以看到:

class IterableExample(object):

    def __iter__(self):
        return self

    def next(self):
        pass

代替

class Iterator(object):
    def next(self):
        pass

class Iterable(object):
    def __iter__(self):
        return Iterator()

但是,在某些情况下,可以从使迭代器与可迭代的对象分离中受益,例如,当您希望有一行项目,但需要更多的“游标”时。例如,当您要使用“当前”和“即将到来”的元素时,可以为这两个元素使用单独的迭代器。或从庞大列表中提取多个线程:每个线程都可以具有自己的迭代器以遍历所有项目。见@雷蒙德@ glglgl的上述回答。

想象一下您可以做什么:

class SmartIterableExample(object):

    def create_iterator(self):
        # An amazingly powerful yet simple way to create arbitrary
        # iterator, utilizing object state (or not, if you are fan
        # of functional), magic and nuclear waste--no kittens hurt.
        pass    # don't forget to add the next() method

    def __iter__(self):
        return self.create_iterator()

笔记:

  • 我将再次重复:迭代器不可迭代。迭代器不能用作for循环中的“源” 。什么for环路主要需要的是__iter__() (即返回与事next())。

  • 当然,for这不是唯一的迭代循环,因此上述内容同样适用于其他一些构造(while...)。

  • 迭代器next()可以抛出StopIteration来停止迭代。但是,它不必永久地迭代或使用其他方式。

  • 在上面的“思想过程”中,_i并不真正存在。我叫这个名字。

  • Python 3.x有一个小的变化:next()现在必须调用方法(不是内置方法)__next__()。是的,一直以来都是这样。

  • 您也可以这样想:可迭代拥有数据,迭代器提取下一项

免责声明:我不是任何Python解释器的开发人员,所以我真的不知道解释器的想法。上面的想法只是从其他解释,实验和Python新手的实际经验中展示了我如何理解该主题。


1
很好-但是我还是有些困惑。我以为您的黄框在说一个for循环需要一个迭代器(“看,一个for循环。看起来像一个迭代器的工作...让我们得到一个。”)。但是然后您在最后的注释中说“迭代器不能用作for循环中的源” ...?
赛车Ta

为什么只pass在代码中输入这些next定义?我假设您的意思是某人必须实现某种方式来获得下一个,因为下一个必须返回某些东西。
nealmcb

@nealmcb是的,我认为这就是我过去的意思。(毕竟,这就是pass目的。)
Alois Mahdal

@AloisMahdal啊,我以前没看过这种用法。当我看到时pass,我认为它是出于语法原因的。我只是在省略号对象上遇到了很有趣的答案:您可以使用它...来指示“稍后再做”块。NotImplemented也可以。
nealmcb

虽然我喜欢您要强调的是迭代器与可迭代对象之间的区别,但此答案本身矛盾。首先,您要编写“迭代器本身也是可迭代的”(与Python文档内容匹配)。但是随后您要写:' 迭代器不可迭代。迭代器不能用作for循环中的“源” 。我的意思是你的答案,否则,我会喜欢的,但我认为从此问题中受益。
丰富

22

可迭代对象是具有__iter__()方法的对象。它可能会迭代多次,例如list()s和tuple()s。

迭代器是要迭代的对象。它由__iter__()方法返回,通过自己的__iter__()方法返回自身,并具有next()方法(__next__()在3.x中)。

迭代是调用此next()响应的过程。__next__()直到它上升StopIteration

例:

>>> a = [1, 2, 3] # iterable
>>> b1 = iter(a) # iterator 1
>>> b2 = iter(a) # iterator 2, independent of b1
>>> next(b1)
1
>>> next(b1)
2
>>> next(b2) # start over, as it is the first call to b2
1
>>> next(b1)
3
>>> next(b1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> b1 = iter(a) # new one, start over
>>> next(b1)
1

真的,这只是穿过容器的对象吗?这有用吗?
thechrishaddad

通常但并非总是如此。生成器,文件或数据库游标只能被迭代一次,因此它们是它们自己的迭代器。
glglgl 2012年

我猜b2不必独立于b1吗?对于这种特殊情况,它是独立的,请确保我可以使其不独立但也可以有效Iterable
2013年

@宾是的。由于和Iterator始终Iterable是它自己的Iterator,两个的调用iter()不一定给出两个独立Iterator的。
glglgl

13

这是我的备忘单:

 sequence
  +
  |
  v
   def __getitem__(self, index: int):
  +    ...
  |    raise IndexError
  |
  |
  |              def __iter__(self):
  |             +     ...
  |             |     return <iterator>
  |             |
  |             |
  +--> or <-----+        def __next__(self):
       +        |       +    ...
       |        |       |    raise StopIteration
       v        |       |
    iterable    |       |
           +    |       |
           |    |       v
           |    +----> and +-------> iterator
           |                               ^
           v                               |
   iter(<iterable>) +----------------------+
                                           |
   def generator():                        |
  +    yield 1                             |
  |                 generator_expression +-+
  |                                        |
  +-> generator() +-> generator_iterator +-+

测验:您知道如何...

  1. 每个迭代器都是可迭代的?
  2. 容器对象的__iter__()方法可以实现为生成器吗?
  3. 具有__next__方法的可迭代对象不一定是迭代器吗?

答案:

  1. 每个迭代器都必须有一个__iter__方法。具有__iter__足够的可迭代性。因此,每个迭代器都是可迭代的。
  2. __iter__被调用时,它应该返回一个迭代器(return <iterator>在上图中)。调用生成器将返回生成器迭代器,它是迭代器的一种。

    class Iterable1:
        def __iter__(self):
            # a method (which is a function defined inside a class body)
            # calling iter() converts iterable (tuple) to iterator
            return iter((1,2,3))
    
    class Iterable2:
        def __iter__(self):
            # a generator
            for i in (1, 2, 3):
                yield i
    
    class Iterable3:
        def __iter__(self):
            # with PEP 380 syntax
            yield from (1, 2, 3)
    
    # passes
    assert list(Iterable1()) == list(Iterable2()) == list(Iterable3()) == [1, 2, 3]
  3. 这是一个例子:

    class MyIterable:
    
        def __init__(self):
            self.n = 0
    
        def __getitem__(self, index: int):
            return (1, 2, 3)[index]
    
        def __next__(self):
            n = self.n = self.n + 1
            if n > 3:
                raise StopIteration
            return n
    
    # if you can iter it without raising a TypeError, then it's an iterable.
    iter(MyIterable())
    
    # but obviously `MyIterable()` is not an iterator since it does not have
    # an `__iter__` method.
    from collections.abc import Iterator
    assert isinstance(MyIterable(), Iterator)  # AssertionError

1
在测验中,我仅了解第一个要点。也就是说,迭代器由于具有__iter__方法而变得可迭代。您能通过编辑此答案来详细说明第二点和第三点吗?
AnV

@AnV:据我了解:re 2 .:__iter__()返回一个迭代器。生成器是迭代器,因此可以用于此目的。关于3 .:我只能在这里猜测,但是我认为如果__iter__()丢失或不返回self,则它不是迭代器,因为迭代器__iter__()必须返回self
glglgl

10

我不知道它是否对任何人都有帮助,但我一直喜欢在脑海中形象化概念以更好地理解它们。因此,当我有一个小儿子时,我用砖块和白皮书形象化了迭代/迭代器的概念。

假设我们在黑暗的房间里,在地板上,我的儿子有砖头。现在,大小,颜色不同的砖都不再重要了。假设我们有5块这样的砖。可以将这5块砖描述为一个对象 -假设是砖块套件。使用此积木工具包,我们可以做很多事情–可以取一个,然后取第二,然后取第三,可以更改积木的位置,将第一个积木放在第二个之上。我们可以用这些做很多事情。因此,这个积木工具包是一个可迭代的对象序列,因为我们可以遍历每个积木并对其进行处理。我们只能做到像我的小儿子-我们可以玩一个在同一时间。所以我再次想像自己这套积木是一个可迭代的

现在请记住,我们在黑暗的房间里。或几乎是黑暗的。问题是我们没有清楚地看到那些砖块,它们是什么颜色,什么形状等等。因此,即使我们想对它们做些事情(也就是遍历它们),我们也不知道到底是什么以及如何做,因为它是太暗了。

我们所能做的就是接近第一个砖块(作为砖块工具包的组成部分),我们可以放一张白色荧光纸,以便我们了解第一个砖块元素的位置。每次我们从工具包中取出一块砖块时,都会将白纸替换为下一块砖块,以便能够在黑暗的房间中看到它。这张白纸只不过是一个迭代器。它也是一个对象。但是,具有可工作和可迭代对象的元素的对象–砖块工具包。

顺便说一下,这解释了我在IDLE中尝试以下操作并遇到TypeError时的早期错误:

 >>> X = [1,2,3,4,5]
 >>> next(X)
 Traceback (most recent call last):
    File "<pyshell#19>", line 1, in <module>
      next(X)
 TypeError: 'list' object is not an iterator

清单X是我们的积木工具包,但不是白纸。我需要先找到一个迭代器:

>>> X = [1,2,3,4,5]
>>> bricks_kit = [1,2,3,4,5]
>>> white_piece_of_paper = iter(bricks_kit)
>>> next(white_piece_of_paper)
1
>>> next(white_piece_of_paper)
2
>>>

不知道是否有帮助,但是对我有帮助。如果有人可以确认/纠正该概念的可视化,我将不胜感激。这将帮助我了解更多信息。


6

可迭代: -这是迭代的迭代; 例如列表,字符串等序列。它也具有__getitem__方法或__iter__方法。现在,如果我们iter()对该对象使用功能,我们将获得一个迭代器。

迭代器:-当我们从iter()函数中获取迭代器对象时;我们调用__next__()方法(在python3中)或简单地next()(在python2中)一一获取元素。此类或此类的实例称为迭代器。

从文档:-

迭代器的使用遍布并统一了Python。在后台,for语句调用  iter() 容器对象。该函数返回一个迭代器对象,该对象定义了__next__() 一次访问一个容器中元素的方法  。当没有更多元素时,  __next__() 引发StopIteration异常,该异常通知for循环终止。您可以__next__() 使用next() 内置函数来调用该  方法  。这个例子展示了它是如何工作的:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    next(it)
StopIteration

例如:

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]


>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
m
a
p
s

4

我认为您不会比文档简单得多,但是我会尝试:

  • 可迭代的东西,可以被重复过。在实践中,它通常表示一个序列,例如具有开始和结束的某种事物,以及某种贯穿其中所有项目的方式。
  • 您可以将Iterator视为辅助伪方法(或伪属性),该伪方法可提供(或保留)iterable中的下一个(或第一个)项。(实际上,它只是一个定义方法的对象next()

  • Merriam-Webster 对该词的定义可能最好地解释了迭代

b:将计算机指令序列重复指定的次数或直到满足条件为止-比较递归


3
iterable = [1, 2] 

iterator = iter(iterable)

print(iterator.__next__())   

print(iterator.__next__())   

所以,

  1. iterable是可以循环对象。例如list,string,tuple等。

  2. iteriterable对象上使用该函数将返回迭代器对象。

  3. 现在,此迭代器对象具有名为__next__(在Python 3中,或仅next在Python 2中)的方法,您可以通过该方法访问iterable的每个元素。

因此,以上代码的输出将是:

1个

2


3

__iter__迭代对象具有每次都实例化新迭代器的方法。

迭代器实现一个__next__返回单个项目的__iter__方法和一个返回的方法self

因此,迭代器也是可迭代的,但是可迭代器不是迭代器。

Luciano Ramalho,流利的Python。


2

在处理迭代器和迭代器之前,决定迭代器和迭代器的主要因素是顺序

序列:序列是数据的集合

可迭代:可迭代是支持__iter__方法的序列类型对象。

Iter方法:Iter方法将序列作为输入并创建一个称为迭代器的对象

迭代器:迭代器是调用next方法并遍历整个序列的对象。在调用下一个方法时,它返回当前遍历的对象。

例:

x=[1,2,3,4]

x是一个由数据收集组成的序列

y=iter(x)

调用iter(x)时,仅当x对象具有iter方法时才返回迭代器,否则会引发异常。如果返回迭代器,则按如下方式分配y:

y=[1,2,3,4]

由于y是迭代器,因此它支持next()方法

调用next方法时,它会一步一步返回列表的各个元素。

返回序列的最后一个元素后,如果再次调用下一个方法,则会引发StopIteration错误

例:

>>> y.next()
1
>>> y.next()
2
>>> y.next()
3
>>> y.next()
4
>>> y.next()
StopIteration

只是一个观察:y = iter(x)并不完全是y = [1,2,3,4],因为y现在是迭代器对象。也许您应该添加注释以阐明不是列表而是迭代器对象,或者更改表示形式。
coelhudo

-6

在Python中,一切都是对象。如果说一个对象是可迭代的,则意味着您可以将对象作为一个集合逐步进行(即迭代)。

例如,数组是可迭代的。您可以使用for循环遍历它们,并从索引0到索引n,n是数组对象的长度减去1。

字典(键/值对,也称为关联数组)也是可迭代的。您可以逐步浏览他们的键。

显然,不是集合的对象是不可迭代的。例如,布尔对象只有一个值为True或False。它不是可迭代的(它是一个可迭代的对象是没有意义的)。

阅读更多。http://www.lepus.org.uk/ref/companion/Iterator.xml


6
不是集合的对象是不可迭代的,通常是不正确的。仅举几个例子,生成器是可迭代的,但不是集合,通过调用iter()标准集合类型创建的迭代器对象是可迭代的,但本身不是集合​​。
Mark Amery 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.