迭代器,迭代和迭代究竟是什么?

the*_*dad 408 python iteration iterator iterable terminology

Python中"iterable","iterator"和"iteration"的最基本定义是什么?

我已阅读多个定义,但我无法确定其确切含义,因为它仍然不会沉入其中.

有人可以帮助我解释外行人的3个定义吗?

agf*_*agf 491

迭代是一个接一个地取出每个项目的通用术语.任何时候使用循环(显式或隐式)来遍历一组项目,即迭代.

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

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

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

无论何时在Python中使用for循环,或者map列表推导等,next都会自动调用该方法以从迭代器中获取每个项目,从而完成迭代过程.

开始学习的好地方是教程迭代器部分和标准类型页面迭代器类型部分.在了解了基础知识之后,请尝试使用Functional Programming HOWTO迭代器部分.

  • @jlh听起来你提出了一个非常固执的dfeault行为.考虑一下`{'a':'hi','b':'bye'}`的长度为2,但不能用0,1或2索引. (4认同)
  • 注意`collections.abc.AsyncIterator` 测试`__aiter__` 和`__anext__` 方法。这是 3.6 中的新增功能。 (3认同)
  • @jlh 为什么`__len__` 必然与迭代相关联?知道某物的长度如何帮助你迭代它? (2认同)
  • @shadowtalker有助于了解哪些索引有效,因此您知道哪些索引可以与`__getitem__`一起使用. (2认同)
  • @shadowtalker。但是字典有一个__iter__方法。我认为jlh所指的是可迭代的对象,这是因为它们定义了:“一个'__getitem__`方法,该方法可以接受从零开始的顺序索引”。 (2认同)

Ray*_*ger 312

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

ITERABLE是:

  • 任何可以循环的东西(即你可以循环遍历字符串或文件)或
  • 任何可能出现在for循环右侧的东西: for x in iterable: ...
  • 你可以打电话的任何东西iter()都会返回一个ITERATOR: iter(obj)
  • 定义的对象__iter__返回一个新的ITERATOR,或者它可能有一个__getitem__适合索引查找的方法.

ITERATOR是一个对象:

  • 在状态下记住迭代期间的位置,
  • 用一种__next__方法:
    • 返回迭代中的下一个值
    • 更新状态以指向下一个值
    • 通过提高来完成信号 StopIteration
  • 这是可自我迭代的(意味着它有一个__iter__返回的方法self).

笔记:

  • __next__Python 3中的方法拼写next在Python 2中,并且
  • 内置函数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
Run Code Online (Sandbox Code Playgroud)

  • @lmiguelvargasf"新鲜",如"新的和未消耗的",而不是"疲惫或部分消耗".这个想法是一个新的迭代器从头开始,而一个部分使用的迭代器从它停止的地方开始. (10认同)
  • 请考虑将“你可以用 `iter()` 调用的任何东西”重新表述为“你可以传递给 `iter()` 的任何东西” (5认同)
  • 你说的新鲜迭代器是什么意思? (2认同)
  • 没有 `__iter__()` 方法的可迭代的例子是什么?(只有一个`__getitem__()`?) (2认同)

Alo*_*dal 94

上面的答案很棒,但正如我所见过的大部分内容,不要强调像我这样的人的区别.

此外,人们倾向于通过像"X是一个有__foo__()方法的对象"之类的定义来获得"过于Pythonic" .这样的定义是正确的 - 它们基于鸭子类型的哲学,但是当试图在简单性中理解概念时,对方法的关注往往会介于两者之间.

所以我添加了我的版本.


用自然语言

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

在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()...)

由于先生obj成功完成了这个测试(通过某种方法返回一个有效的迭代器),我们用形容词奖励他:你现在可以称他为"可迭代的先生obj".

但是,在简单的情况下,通常不会分别使用迭代器和迭代.所以你定义了一个对象,它也是它自己的迭代器.(Python并不真正关心_i发布的obj并不是那么闪亮,而是它obj本身.)

这就是为什么在我见过的大多数例子中(以及令我一直困惑的东西),你可以看到:

class IterableExample(object):

    def __iter__(self):
        return self

    def next(self):
        pass
Run Code Online (Sandbox Code Playgroud)

代替

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

class Iterable(object):
    def __iter__(self):
        return Iterator()
Run Code Online (Sandbox Code Playgroud)

但是,有些情况下,您可以从迭代器中分离迭代器,例如当您想拥有一行项目,但更多的是"游标"时.例如,当您想使用"当前"和"即将到来"元素时,可以为两者分别设置迭代器.或者从一个巨大的列表中拉出多个线程:每个线程都可以拥有自己的迭代器来遍历所有项目.请参阅上面的@ Raymond@ 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()
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我将再次重复:迭代器不可迭代.迭代器不能用作for循环中的"源" .for主要需要的是什么循环__iter__() (返回一些东西next()).

  • 当然,for不是唯一的迭代循环,所以上面也适用于其他一些结构(while...).

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

  • 在上面的"思考过程"中,_i并不存在.我已经编造了这个名字.

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

  • 你也可以这样想:iterable有数据,iterator拉下一个项目

免责声明:我不是任何Python解释器的开发人员,所以我真的不知道解释器"思考"了什么.上面的思考只是演示了我如何从Python新手的其他解释,实验和现实生活经验中理解这个主题.

  • 虽然我喜欢您强调迭代器和可迭代之间的区别,但这个答案是自相矛盾的。首先你写“迭代器本身也是可迭代的”(这与[Python文档](https://docs.python.org/3/library/stdtypes.html#iterator-types)中的内容相匹配)。但稍后你会写:“*迭代器不可迭代*”。迭代器不能用作“for”循环中的“源”。我明白你的答案的要点,并且喜欢它,但我认为解决这个问题会对你有所帮助。 (6认同)
  • 这很棒 - 但我仍然有点困惑。我以为你的黄框是说`for` 循环需要一个迭代器(“看,一个for 循环。看起来像一个迭代器的工作......让我们来一个。”)。但是你在最后的注释中说“迭代器不能用作 `for` 循环中的源”......? (3认同)
  • @AloisMahdal 啊,我以前没见过这种用法。当我看到 `pass` 时,我认为它是出于语法原因而存在的。我刚刚在 [ellipsis object](http://stackoverflow.com/a/6189281/507544) 上找到了答案,这些答案非常有趣:您可以使用 `...` 来表示“稍后待办”块。`NotImplemented` 也可用。 (2认同)

glg*_*lgl 21

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

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

迭代是调用此next()resp 的过程.__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
Run Code Online (Sandbox Code Playgroud)

  • 经常,但并非总是如此。生成器、文件或数据库游标只能迭代一次,因此是它们自己的迭代器。 (2认同)
  • @PatrickT 三人:是的。试试吧。`for i in [1,3,4,6]: print(i)` / `for i in {1,3,4,6}: print(i)` / `for i in (1,3,4) ,6): 打印(i)`。另外,请查看相应的文档。语言规范。 (2认同)
  • @PatrickT 这甚至可能取决于Python 版本和执行历史记录(例如对象ID/地址、它们的类型等)。如果您需要对集合进行排序,请参阅[此问题](/sf/ask/115777931/)中有关有序集合的更多信息。 (2认同)

Nik*_*aev 9

我不知道它是否对任何人有所帮助,但我总是喜欢想象一下我头脑中的概念,以便更好地理解它们.因此,当我有一个小儿子时,我用砖和白纸可视化可迭代/迭代器概念.

假设我们在黑暗的房间和地板上,我们的儿子都有砖块.不同尺寸,颜色的砖现在无关紧要.假设我们有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
Run Code Online (Sandbox Code Playgroud)

这里的清单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
>>>
Run Code Online (Sandbox Code Playgroud)

不知道它是否有帮助,但它对我有帮助.如果有人能够确认/纠正这个概念的可视化,我将不胜感激.这将有助于我了解更多.


AXO*_*AXO 7

这是我的备忘单:

 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 +-+
Run Code Online (Sandbox Code Playgroud)

测验:你看到了......

  • 每个迭代器都是可迭代的?
  • 容器对象的__iter__()方法可以实现为生成器吗?
  • 迭代加__next__方法不一定是迭代器吗?

  • 在测验中,我只理解了第一个要点。即迭代器成为可迭代对象,因为它具有“__iter__”方法。您能否通过编辑此答案来详细说明第二点和第三点 (2认同)
  • 有趣的是,“isinstance(MyIterable(), collections.abc.Iterable)”也是 False。@_@ (2认同)

Kim*_*ais 5

我不认为你可以得到它比文档简单得多,但是我会尝试:

  • Iterable是可以迭代的东西。在实践中,它通常意味着一个序列,例如有一个开始和一个结束以及某种方式来遍历其中的所有项目。
  • 您可以将Iterator视为一个辅助伪方法(或伪属性),它给出(或保存)iterable 中的下一个(或第一个)项目。(实际上它只是一个定义方法的对象next()

  • 迭代可能最好用 Merriam-Webster这个词的定义来解释:

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


Vic*_*bot 5

可迭代: -这是迭代的迭代; 例如列表,字符串等序列。它也具有__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
Run Code Online (Sandbox Code Playgroud)

例如:

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
Run Code Online (Sandbox Code Playgroud)


tec*_*kuz 5

Iterables有一个__iter__方法,每次都实例化一个新的迭代器。

迭代器实现了一个__next__返回单个项目的__iter__方法,以及一个返回self.

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

Luciano Ramalho,流利的 Python。


Yil*_*maz 5

迭代器是实现iternext方法的对象。如果定义了这些方法,我们就可以使用 for 循环或推导式。

class Squares:
    def __init__(self, length):
        self.length = length
        self.i = 0
        
    def __iter__(self):
        print('calling __iter__') # this will be called first and only once
        return self
    
    def __next__(self): 
        print('calling __next__') # this will be called for each iteration
        if self.i >= self.length:
            raise StopIteration
        else:
            result = self.i ** 2
            self.i += 1
            return result
Run Code Online (Sandbox Code Playgroud)

迭代器会耗尽。这意味着在迭代项目后,您不能重复,您必须创建一个新对象。假设您有一个类,其中包含城市属性并且您想要迭代。

class Cities:
    def __init__(self):
        self._cities = ['Brooklyn', 'Manhattan', 'Prag', 'Madrid', 'London']
        self._index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self._index >= len(self._cities):
            raise StopIteration
        else:
            item = self._cities[self._index]
            self._index += 1
            return item
Run Code Online (Sandbox Code Playgroud)

Cities 类的实例是一个迭代器。但是,如果您想在城市上重复,则必须创建一个新对象,这是一项昂贵的操作。您可以将该类分为 2 个类:一个返回城市,第二个返回一个迭代器,该迭代器将城市作为初始化参数。

class Cities:
    def __init__(self):
        self._cities = ['New York', 'Newark', 'Istanbul', 'London']        
    def __len__(self):
        return len(self._cities)



class CityIterator:
    def __init__(self, city_obj):
        # cities is an instance of Cities
        self._city_obj = city_obj
        self._index = 0
        
    def __iter__(self):
        return self
    
    def __next__(self):
        if self._index >= len(self._city_obj):
            raise StopIteration
        else:
            item = self._city_obj._cities[self._index]
            self._index += 1
            return item
Run Code Online (Sandbox Code Playgroud)

现在如果我们需要创建一个新的迭代器,我们不必再次创建数据,即城市。我们创建城市对象并将其传递给迭代器。但我们仍在做额外的工作。我们可以通过仅创建一个类来实现这一点。

Iterable是一个实现了iterable 协议的Python 对象。它只需要__iter__()返回迭代器对象的新实例。

class Cities:
    def __init__(self):
        self._cities = ['New York', 'Newark', 'Istanbul', 'Paris']
        
    def __len__(self):
        return len(self._cities)
    
    def __iter__(self):
        return self.CityIterator(self)
    
    class CityIterator:
        def __init__(self, city_obj):
            self._city_obj = city_obj
            self._index = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self._index >= len(self._city_obj):
                raise StopIteration
            else:
                item = self._city_obj._cities[self._index]
                self._index += 1
                return item
Run Code Online (Sandbox Code Playgroud)

迭代器有__iter____next__,可迭代对象有__iter__,所以我们可以说迭代器也是可迭代对象,但它们是会耗尽的可迭代对象。另一方面,可迭代永远不会耗尽,因为它们总是返回一个新的迭代器,然后用于迭代

您会注意到可迭代代码的主要部分在迭代器中,而可迭代本身只不过是一个允许我们创建和访问迭代器的额外层。

迭代可迭代对象

Python 有一个内置函数iter(),它调用__iter__(). 当我们迭代一个可迭代对象时,Python 会调用iter()返回一个迭代器,然后它开始使用__next__()迭代器来迭代数据。

注意,在上面的示例中,Cities 创建了一个可迭代对象,但它不是序列类型,这意味着我们无法通过索引获取城市。为了解决这个问题,我们应该添加__get_item__到 Cities 类中。

class Cities:
    def __init__(self):
        self._cities = ['New York', 'Newark', 'Budapest', 'Newcastle']
        
    def __len__(self):
        return len(self._cities)
    
    def __getitem__(self, s): # now a sequence type
        return self._cities[s]
    
    def __iter__(self):
        return self.CityIterator(self)
    
    class CityIterator:
        def __init__(self, city_obj):
            self._city_obj = city_obj
            self._index = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self._index >= len(self._city_obj):
                raise StopIteration
            else:
                item = self._city_obj._cities[self._index]
                self._index += 1
                return item
Run Code Online (Sandbox Code Playgroud)