什么是迭代器、可迭代器和迭代?

什么是“可迭代”、“迭代器”和“迭代”?在Python中?它们是如何定义的?

384831 次浏览

iterable是一个具有__iter__()方法的对象。它可以迭代多次,比如list()s和tuple()s。

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

迭代是调用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

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

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

  • 迭代可能最好由韦氏词典这个词的定义:

b:计算机指令序列的重复 满足条件的次数-比较递归

迭代是一个通用术语,指的是一个接一个地获取某样东西的每一项。任何时候使用循环,显式或隐式,遍历一组项,这就是迭代。

在Python中,可迭代的迭代器有特定的含义。

可迭代的是一个对象,它有一个__iter__方法,该方法返回一个迭代器,或者它定义了一个__getitem__方法,该方法可以接受从0开始的顺序索引(当索引不再有效时引发IndexError)。因此,可迭代的是一个对象,你可以从中得到迭代器

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

每当你在Python中使用for循环,或map,或列表推导式等,next方法将被自动调用,以从迭代器中获取每一项,从而经历迭代的过程。

迭代器部分的内容标准类型页的迭代器类型部分是开始学习的好地方。在你理解了基本原理之后,试试函数式编程方法的迭代器部分

以下是我在教授Python课程时使用的解释:

ITERABLE是:

  • 任何可以循环的东西(例如,你可以循环一个字符串或文件)或
  • 任何可以出现在for循环右边的东西:for x in iterable: ...
  • 任何你可以用iter()调用的将返回一个ITERATOR: iter(obj)
  • 一个对象,它定义了返回一个新的ITERATOR的__iter__, 或者它可能有一个适合于索引查找的__getitem__方法

ITERATOR是一个对象:

  • 在迭代过程中,state会记住它的位置,
  • 使用__next__方法:
    • 返回迭代中的下一个值
    • 更新状态以指向下一个值
    • 通过抛出StopIteration来表示完成
    • 李< / ul > < / >
    • 它是self-iterable(意味着它有一个返回self__iter__方法)。

    注:

    • Python 3中的__next__方法在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
    

上面的答案很好,但就我所见,不要对像我这样的人太强调区别

此外,人们倾向于把“X是一个具有__foo__()方法的对象”这样的定义放在前面,从而变得“过于python化”。这样的定义是正确的——它们基于duck-typing哲学,但是在试图理解概念的简单性时,对方法的关注往往会变得偏颇。

所以我添加了我的版本。


在自然语言中,

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

在Python中,

  • 可迭代的是一个对象,嗯,是可迭代的,简单地说,意思是 它可以在迭代中使用,例如使用for循环。如何?通过使用迭代器

  • <李> < p >…而迭代器是一个对象,它定义了怎么做 迭代——特别是下一个是什么元素。这就是为什么它一定是 # EYZ0方法。李< / p > < / >

迭代器本身也是可迭代的,区别在于它们的__iter__()方法返回相同的对象(self),而不管它的项是否已被之前对next()的调用所消耗。


那么Python解释器看到for x in obj:语句时是怎么想的呢?

看,一个for循环。看起来像是迭代器的工作…让我们买一个. ... 这里有个obj的家伙,让我们问问他 < p >。obj,你有迭代器吗?”(…调用iter(obj),它调用 obj.__iter__(),它很高兴地分发了一个闪亮的新迭代器_i

好吧,这很简单……让我们开始迭代。(# EYZ0……# EYZ0…)

由于Mr. obj在这个测试中成功了(通过让某个方法返回一个有效的迭代器),我们用一个形容词来奖励他:你现在可以称他为“可迭代的Mr. obj”。

然而,在简单的情况下,将迭代器和iterable分开使用通常没有什么好处。所以你定义了只有一个对象,它也是它自己的迭代器。(Python并不真正关心由obj传递的_i是不是那么闪亮,而是关心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()

不过,在有些情况下,将迭代器与可迭代对象分开可以带来好处,比如当你想要有一行项,但有更多的“游标”时。例如,当您想要处理“当前”和“即将”元素时,可以为这两个元素使用单独的迭代器。或者从一个巨大的列表中抽取多个线程:每个线程都有自己的迭代器来遍历所有项。见上面的@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()

注:

  • 我再重复一次:迭代器不是可迭代的。迭代器不能用作 for循环中的“源”。for循环主要需要的是__iter__()

  • .(返回next()) 当然,for并不是唯一的迭代循环,所以以上也适用于其他一些迭代循环

    . (while
  • 迭代器的next()可以抛出StopIteration来停止迭代。不需要, 不过,它可以永远迭代或使用其他方法

  • 在上面的“思维过程”中,_i并不真正存在。那个名字是我编的。

  • Python 3中有一个小变化。next()方法(不是内置的) 必须称为__next__()。是的,它应该一直是这样的。

    你也可以这样想:iterable有数据,iterator取出下一个 李项< / p > < / >

我不是任何Python解释器的开发人员,所以我真的不知道解释器“在想什么”。上面的思考仅仅是我从其他解释、实验和一个Python新手的实际经验中对这个主题的理解。

我不知道这是否对任何人都有帮助,但我总是喜欢在脑子里把概念形象化,以便更好地理解它们。所以,就像我有一个小儿子一样,我用砖块和白纸来想象迭代器/迭代器的概念。

假设我们在黑暗的房间里,地板上有给我儿子的砖头。砖的大小、颜色不同,现在都无所谓了。假设我们有5块这样的砖。这5块砖可以被描述为对象 -让我们说< em >砖包< / em >。我们可以用这个砖包做很多事情——可以拿一块,然后拿第二块,然后第三块,可以换砖的位置,把第一块砖放在第二块砖上面。我们可以用它做很多事情。因此,这个砖块工具包是iterable对象序列,因为我们可以遍历每个砖块并对其做一些事情。我们只能像我的小儿子那样做——我们可以玩一个砖块一次。所以我再一次把自己想象成一个可迭代的

现在请记住,我们是在一个黑暗的房间里。或者几乎是黑暗的。问题是我们看不清这些砖,它们是什么颜色,什么形状等等。所以即使我们想对它们做些什么——也就是遍历它们——我们也不知道要做什么,怎么做,因为太黑了。

我们能做的是靠近第一块砖-作为一个砖套件的元素-我们可以放一张白色荧光纸,以便我们看到第一块砖元素在哪里。每次我们从工具箱中取出一块砖,我们就把这张白纸换成下一块砖,这样就能在黑暗的房间里看到它。这张白纸不过是一个迭代器。它是对象。而是一个我们可以使用可迭代对象中的元素的对象——bricks工具包。

顺便说一下,这解释了我早期的错误,当我在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
>>>

不知道有没有帮助,但对我有帮助。如果有人能确认/纠正这个概念的可视化,我会很感激。这会帮助我了解更多。

iterable = [1, 2]


iterator = iter(iterable)


print(iterator.__next__())


print(iterator.__next__())

所以,

  1. iterable是一个对象,它可以被循环覆盖。例如,列表,字符串,元组等。

  2. iterable对象上使用iter函数将返回一个迭代器对象

  3. 现在这个迭代器对象有一个名为__next__的方法(在python3中,或者在python2中只是next),通过这个方法,你可以访问iterable的每个元素

< p >, 以上代码的输出将是:

1

2

在处理可迭代对象和迭代器之前,决定可迭代对象和迭代器的主要因素是序列

序列:序列是数据的集合

Iterable: Iterable是支持__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

下面是我的小抄:

 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. Here is an example:

    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
    

可迭代的:-可迭代的东西是可迭代的;比如序列,比如列表,字符串等等。 此外,它有__getitem__方法或__iter__方法。现在如果我们在该对象上使用iter()函数,我们将得到一个迭代器。< / p >

迭代器:-当我们从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

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

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

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

Luciano Ramalho,精通Python。

其他人已经全面地解释了可迭代的迭代器是什么,所以我将尝试对发电机做同样的事情。

恕我直言,理解生成器的主要问题是“生成器”这个词的混淆使用,因为这个词是在2种不同的意思:中使用的

  1. 作为创建(生成)迭代器的工具
    • 返回迭代器的函数的形式(即在它的主体中有yield语句),
    • 格式为生成器表达式
  2. 作为该工具使用的结果,即产生的迭代器。
    (这意味着发电机迭代器的特殊形式-单词“generator”指向这个迭代器是如何创建的。)

生成器作为1st型工具:

In[2]: def my_generator():
...:     yield 100
...:     yield 200


In[3]: my_generator
Out[3]: <function __main__.my_generator()>
In[4]: type(my_generator)
Out[4]: function

生成器作为结果(即迭代器)的使用此工具:

In[5]: my_iterator = my_generator()
In[6]: my_iterator
Out[6]: <generator object my_generator at 0x00000000053EAE48>
In[7]: type(my_iterator)
Out[7]: generator

生成器作为2nd类型的工具 -与这个工具的产生的迭代器没有区别:

In[8]: my_gen_expression = (2 * i for i in (10, 20))
In[9]: my_gen_expression
Out[9]: <generator object <genexpr> at 0x000000000542C048>
In[10]: type(my_gen_expression)
Out[10]: generator

迭代器是实现iter下一个方法的对象。如果定义了这些方法,则可以使用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

迭代器会耗尽。这意味着在你遍历项目之后,你不能重复,你必须创建一个新对象。假设你有一个类,它包含cities属性,你想要遍历。

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

类Cities的实例是一个迭代器。然而,如果你想在城市上重复,你必须创建一个新对象,这是一个昂贵的操作。你可以把这个类分成两个类:一个返回城市,第二个返回一个迭代器,它将城市作为初始参数。

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

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

可迭代的是一个Python对象,它实现了iterable协议。它只需要返回一个迭代器对象的新实例的__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

迭代器有__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

下面是使用collections.abc的另一个视图。这个视图在第二次或以后可能会有用。

collections.abc我们可以看到下面的层次结构:

builtins.object
Iterable
Iterator
Generator

例如,Generator是由Iterator派生的Iterable是由基对象派生的。

因此,

  • 每个迭代器都是可迭代对象,但并不是每个可迭代对象都是迭代器。例如,[1, 2, 3]range(10)是可迭代对象,而不是迭代器。x = iter([1, 2, 3])是一个迭代器而且是一个迭代器
  • 迭代器和生成器之间也存在类似的关系。
  • 在迭代器或生成器上调用iter()将返回自身。因此,如果it是一个迭代器,那么iter(it) is it为True。
  • 实际上,像[2 * x for x in nums]这样的列表推导式或像for x in nums:这样的for循环,就好像是在可迭代对象(nums)上调用iter(),然后使用该迭代器迭代nums。因此,下面所有的函数都是等价的(比如,nums=[1, 2, 3]):
    • # EYZ0
    • # EYZ0
    • # EYZ0:
    • # EYZ0

对我来说,Python的glossery对这些问题最有帮助,例如对于iterable,它说:

每次能够返回一个成员的对象。可迭代对象的例子包括所有序列类型(如list、str和tuple)和一些非序列类型,如dict、文件对象,以及您使用iter()方法或getitem()方法定义的任何类的对象,这些方法实现了sequence语义。

可迭代对象可用于for循环和许多其他需要序列的地方(zip(), map(),…)。当一个可迭代对象作为参数传递给内置函数iter()时,它将返回该对象的迭代器。这个迭代器只适用于遍历一组值。当使用可迭代对象时,通常不需要调用iter()或自己处理迭代器对象。for语句自动为您完成这一工作,创建一个临时的未命名变量来在循环期间保存迭代器。请参见迭代器、序列和生成器。

    可迭代的是一个具有iter()方法的对象,该方法返回一个迭代器。这是可以循环的。 示例:列表是可迭代的,因为我们可以遍历列表BUT不是迭代器
  • 迭代器是一个可以从中获取迭代器的对象。它是一个具有状态的对象,以便在迭代过程中记住它所处的位置

要查看对象是否有这个方法iter(),可以使用下面的函数。

ls = ['hello','bye']
print(dir(ls))

输出

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']


正如你所看到的,有iter(),这意味着它是一个可迭代对象,但不包含下一个()方法,这是迭代器对象的一个特性

无论何时在Python中使用for循环或map或列表推导式,下一个方法都会自动调用以从迭代中获取每一项