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

时间:2012-03-27 06:03:38

标签: python iterator iteration terminology iterable

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

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

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

18 个答案:

答案 0 :(得分:450)

迭代是一个接一个地取出每件物品的通用术语。无论何时使用显式或隐式循环来遍历一组项目,即迭代。

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

可迭代是一个具有__iter__方法的对象,该方法返回迭代器,或者定义可以采用顺序的__getitem__方法索引从零开始(当索引不再有效时引发IndexError)。所以 iterable 是一个可以从中获取迭代器的对象。

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

每当您在Python中使用for循环或map或列表推导等时,系统会自动调用next方法以从中获取每个项目迭代器,因此经历了迭代的过程。

开始学习的好地方是iterators section of the tutorialiterator types section of the standard types page。了解基础知识后,请尝试iterators section of the Functional Programming HOWTO

答案 1 :(得分:292)

以下是我在Python课程教学中使用的解释:

ITERABLE是:

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

ITERATOR是一个对象:

  • 状态,记住迭代期间的状态,
  • 使用__next__方法:
    • 返回迭代中的下一个值
    • 更新状态以指向下一个值
    • 通过提出StopIteration
    • 发出信号
  • ,这是可自我迭代的(意味着它有一个返回__iter__的{​​{1}}方法。

注意:

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

例如:

next()

答案 2 :(得分:92)

上述答案很棒,但正如我所看到的大多数情况一样,不要强调区别对于像我这样的人来说。

此外,人们倾向于通过像“X是具有__foo__()方法的对象”之类的定义来获得“过于Pythonic”。这样的定义是正确的 - 它们基于鸭子类型的哲学,但是当试图以简单的方式理解这个概念时,对方法的关注往往会介入。

所以我添加了我的版本。


用自然语言,

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

在Python中,

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

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

迭代器本身也是可迭代的,区别在于它们的__iter__()方法返回相同的对象(self),无论先前调用{{1}是否已使用它的项目}。


那么Python解释器在看到next()语句时会怎么想?

  

看,for x in obj:循环。看起来像迭代器的工作......让我们得到一个。 ...   有这个for家伙,所以让我们问他。

     

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

     

好的,这很容易......让我们开始迭代吧。 (_i ... x = _i.next() ...)

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

但是,在简单的情况下,通常不会分别使用迭代器和迭代。因此,您只定义一个对象,它也是它自己的迭代器。 (Python并不关心obj发出的_i并不是那么闪亮,而只是obj本身。)

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

obj

而不是

class IterableExample(object):

    def __iter__(self):
        return self

    def next(self):
        pass

但是,有些情况下,您可以从迭代器中分离迭代器,例如当您想拥有一行项目,但需要更多“游标”时。例如,当您想使用“当前”和“即将到来”元素时,可以为两者分别设置迭代器。或者从一个巨大的列表中拉出多个线程:每个线程都可以拥有自己的迭代器来遍历所有项目。请参阅上面的@Raymond's@glglgl's解答。

想象一下你能做什么:

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

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

注意:

  • 我将再次重复:迭代器不可迭代。迭代器不能用作 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 ...)。

  • Iterator的while可以抛出StopIteration来停止迭代。不必, 但是,它可以永久迭代或使用其他方式。

  • 在上面的“思考过程”中,next()并不存在。我已经填了这个名字。

  • Python 3.x中有一个小变化:_i方法(不是内置的)现在 必须被称为next()。是的,它应该一直都是这样。

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

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

答案 3 :(得分:20)

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

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

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

示例:

{{1}}

答案 4 :(得分: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

这里的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
>>>

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

答案 5 :(得分: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 +-+

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

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

答案 6 :(得分:3)

我不认为你可以比documentation更简单,但是我会尝试:

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

  • 的对象 Merriam-Webster definition of the word 最好解释
  • 迭代

  

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

答案 7 :(得分:3)

Iterable : - 可迭代的东西是可迭代的;像列表,字符串等序列   它还有__getItem__()方法或返回迭代器的iter()函数。

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

来自docs: -

迭代器的使用遍及并统一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

答案 8 :(得分:2)

iterable = [1, 2] 

iterator = iter(iterable)

print(iterator.__next__())   

print(iterator.__next__())   

所以,

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

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

  3. 现在这个迭代器对象的方法名为__next__(在Python 3中,或者只是Python 2中的next),您可以通过它访问每个可迭代的元素。

  4. 所以,     上面代码的输出将是:

    1

    2

答案 9 :(得分:1)

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

序列:序列是数据的集合

Iterable:Iterable是支持Iter方法的序列类型对象。

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

Iterator:Iterator是调用next方法并横向遍历序列的对象。在调用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

答案 10 :(得分:1)

  

可迭代对象具有__iter__方法,该方法每次都实例化一个新的迭代器。

     

迭代器实现返回单个项目的__next__方法和返回__iter__的{​​{1}}方法。

     

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

Luciano Ramalho,流利的Python。

答案 11 :(得分:1)

迭代器是实现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

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

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 类的实例是一个迭代器。但是,如果您想重复城市,则必须创建一个新对象,这是一项昂贵的操作。您可以将类分为 2 个类:一个返回城市,第二个返回一个迭代器,该迭代器将城市作为 init 参数。

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

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

Iterable 是一个 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

答案 12 :(得分:1)

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

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

    function* parser(some_string) {
        let stack = "", pars = 0;
        for (let i = 0; i < some_string.length; i++) {
            if (some_string[i] == "(") {
                if ((pars == 0) && (stack.length > 0)) {
                    yield stack;
                    stack = "";
                }
                stack += some_string[i];
                pars += 1;
            } else if (some_string[i] == ")") {
                stack += some_string[i];
                pars -= 1;
                if ((pars == 0) && (stack.length > 0)) {
                    yield stack;
                    stack = "";
                }
            } else if (i == some_string.length - 1) {
                if (stack.length > 0) {
                    yield stack;
                    stack = "";
                }
            } else {
                stack += some_string[i];
            }
    
        }
    }
    
    let string = "foo(bar)baz(blim)(hhh(kkk)thfgg)";
    
    const it = parser(string);
    
    for(item of it) {
        console.log(item);
    }

输出

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

如您所见,iter() 表示它是一个可迭代对象,但不包含 next() 方法,这是迭代器对象

每当您在 Python 中使用 for 循环或映射或列表推导式时,都会自动调用 next 方法以从迭代中获取每个项目

答案 13 :(得分:0)

其他人已经全面解释了什么是 iterable iterator ,所以我将尝试使用 generators 做同样的事情。

恕我直言,理解生成器的主要问题是混淆了“ generator”一词的使用,因为该词有两种不同的含义:

  1. 作为创建(生成)迭代器工具
    • 函数的形式返回迭代器(即,其主体中带有yield语句)
    • 生成器表达式形式的
  2. 作为使用该工具的结果,即结果迭代器。
    (在这个意义上, generator iterator 的一种特殊形式-“ generator”一词指出了该迭代器的创建方式。) / li>

Generator作为1 st 类型的工具:

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

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

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

Generator作为2 nd 类型的工具 –与该工具的结果迭代器没有区别:

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

答案 14 :(得分:0)

这是另一个使用 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 循环,就像在可迭代对象 (iter()) 上调用 nums 然后进行迭代一样使用该迭代器在 nums 上。因此,以下所有内容在功能上都是等效的(例如,nums=[1, 2, 3]):
    • for x in nums:
    • for x in iter(nums):
    • for x in iter(iter(nums))
    • for x in iter(iter(iter(iter(iter(nums))))):

答案 15 :(得分:0)

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

<块引用>

一个能够一次返回一个成员的对象。可迭代对象的示例包括所有序列类型(例如列表、字符串和元组)和一些非序列类型,例如 dict、文件对象以及您使用 iter() 方法定义的任何类的对象或使用实现 Sequence 语义的 getitem() 方法。

<块引用>

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

答案 16 :(得分:-3)

示例如下: 类 Square(): def init(self, max): self.max =max 自身力量=0 def iter(self): 回归自我 def next(self):

    if(self.power<=self.max):
        result = 2**self.power
        self.power +=1
        return result

    else:
        raise StopIteration

square = Square(5) 对于我在广场: 打印(i)

答案 17 :(得分:-6)

在Python中,一切都是对象。当一个对象被称为可迭代时,这意味着您可以将对象作为集合单步执行(即迭代)。

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

字典(键/值对,也称为关联数组)也是可迭代的。你可以逐步完成他们的钥匙。

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

了解更多。 http://www.lepus.org.uk/ref/companion/Iterator.xml