Python的迭代器和生成器

先说迭代器,对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是python的内置函数。iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。比如:

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

上面说的都是python自带的容器对象,它们都实现了相应的迭代器方法,那如果是自定义类需要遍历怎么办?方法很简单,对这个类AClass,实现一个__iter__(self)方法,使其返回一个带有__next__(self)方法的对象就可以了。如果你在AClass刚好也定义了__next__(self)方法(一般使用迭代器都会定义),那在__iter__里只要返回self就可以。废话少说,先上代码:

class Fib(object):
  def __init__(self, max):
    super(Fib, self).__init__()
    self.max = max

  def __iter__(self):
    self.a = 0
    self.b = 1
    return self

  def __next__(self):
    fib = self.a
    if fib > self.max:
      raise StopIteration
    self.a, self.b = self.b, self.a + self.b
    return fib

def main():
  fib = Fib(100)
  for i in fib:
    print(i)

if __name__ == '__main__':
  main()

简单讲下代码会干什么,定义了一个Fib类,用于生成fibonacci序列。用for遍历时会逐个打印生成的fibonacci数,max是生成的fibonacci序列中数字大小的上限。

在类的实现中,定义了一个__iter__(self)方法,这个方法是在遍历时被iter()调用,返回一个迭代器。因为在遍历的时候,是直接调用的python内置函数iter(),由iter()通过调用__iter__(self)获得对象的迭代器。有了迭代器,就可以逐个遍历元素了。而逐个遍历的时候,也是使用内置的next()函数通过调用对象的__next__(self)方法对迭代器对象进行遍历。所以要实现__iter__(self)和__next__(self)。而且因为实现了__next__(self),所以在实现__iter__(self)的时候,直接返回self就可以。

为了更好理解,我再简单重复下上面说的那一段:在循环遍历自定义容器对象时,会使用python内置函数iter()调用遍历对象的__iter__(self)获得一个迭代器,之后再循环对这个迭代器使用next()调用迭代器对象的__next__(self)。__iter__只会被调用一次,而__next__会被调用 n 次。

下面说生成器。

生成器(Generator)是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在需要返回数据的时候使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)。以下示例演示了生成器可以很简单的创建出来:

>>> def reverse(data):
...   for index in range(len(data)-1, -1, -1):
...     yield data[index]
...
>>> for char in reverse('hello'):
...   print(char)
...
o
l
l
e
h

关于迭代器和生成器的区别,生成器能做到迭代器能做的所有事,而且因为自动创建了__iter__()和 next()方法,生成器显得特别简洁,而且生成器也是高效的。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出StopIteration异常。一个带有yield的函数就是一个 生成器,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用next()(在 for 循环中会自动调用next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个yield语句就会中断,并返回一个迭代值,下次执行时从yield的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被yield中断了数次,每次中断都会通过yield返回当前的迭代值(yield暂停一个函数,next()从其暂停处恢复其运行)。

另外对于生成器,python还提供了一个生成器表达式:类似与一个yield值的匿名函数。表达式本身看起来像列表推到, 但不是用方括号而是用圆括号包围起来:

>>> unique_characters = {'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}
>>> gen = (ord(c) for c in unique_characters)
>>> gen
<generator object <genexpr> at 0x7f2be4668678>
>>> for i in gen:
...   print(i)
...
69
79
83
77
82
78
89
68
>>>

如果需要,可以将生成器表达式传给tuple、list或是set来迭代所有的值并且返回元组、列表或是集合。在这种情况下,不需要一对额外的括号 ———— 直接将生成器表达式 ord(c) for c in unique_characters传给tuple()等函数就可以了, Python 会推断出它是一个生成器表达式。

最后,为什么要使用生成器?因为效率。使用生成器表达式取代列表解析可以同时节省 cpu 和 内存(ram)。如果你构造一个列表的目的仅仅是传递给别的函数,(比如 传递给tuple()或者set()), 那就用生成器表达式替代吧!

以上所述就是本文的全部内容了,希望大家能够喜欢。

(0)

相关推荐

  • Python迭代器和生成器介绍

    迭代器 迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration. 在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作. 常用的几个内建数据结构tuple.list.set.dict都支持迭代器,字符串也可以使用迭代操作. 你也可以自己实现一个迭代器,如上所述,只需要在类的__iter__方法中

  • 解析Python中的生成器及其与迭代器的差异

    生成器 生成器是一种迭代器,是一种特殊的函数,使用yield操作将函数构造成迭代器.普通的函数有一个入口,有一个返回值:当函数被调用时,从入口开始执行,结束时返回相应的返回值.生成器定义的函数,有多个入口和多个返回值:对生成器执行next()操作,进行生成器的入口开始执行代码,yield操作向调用者返回一个值,并将函数挂起:挂起时,函数执行的环境和参数被保存下来:对生成器执行另一个next()操作时,参数从挂起状态被重新调用,进入上次挂起的执行环境继续下面的操作,到下一个yield操作时重复上面

  • Python的迭代器和生成器使用实例

    一.迭代器Iterators 迭代器仅是一容器对象,它实现了迭代器协议.它有两个基本方法: 1)next方法 返回容器的下一个元素 2)__iter__方法 返回迭代器自身 迭代器可使用内建的iter方法创建,见例子: 复制代码 代码如下: >>> i = iter('abc') >>> i.next() 'a' >>> i.next() 'b' >>> i.next() 'c' >>> i.next() Trace

  • Python 迭代器与生成器实例详解

    Python 迭代器与生成器实例详解 一.如何实现可迭代对象和迭代器对象 1.由可迭代对象得到迭代器对象 例如l就是可迭代对象,iter(l)是迭代器对象 In [1]: l = [1,2,3,4] In [2]: l.__iter__ Out[2]: <method-wrapper '__iter__' of list object at 0x000000000426C7C8> In [3]: t = iter(l) In [4]: t.next() Out[4]: 1 In [5]: t.

  • python的迭代器与生成器实例详解

    本文以实例详解了python的迭代器与生成器,具体如下所示: 1. 迭代器概述:   迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.   1.1 使用迭代器的优点   对于原生支持随机访问的数据结构(如tuple.list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值).但对于无法随机访问的数据结构(比

  • 举例讲解Python中的迭代器、生成器与列表解析用法

    迭代器:初探 上一章曾经提到过,其实for循环是可用于任何可迭代的对象上的.实际上,对Python中所有会从左至右扫描对象的迭代工具而言都是如此,这些迭代工具包括了for循环.列表解析.in成员关系测试以及map内置函数等. "可迭代对象"的概念在Python中是相当新颖的,基本这就是序列观念的通用化:如果对象时实际保存的序列,或者可以再迭代工具环境中一次产生一个结果的对象,那就看做是可迭代的. >>文件迭代器 作为内置数据类型的文件也是可迭代的,它有一个名为__next_

  • 深入讲解Python中的迭代器和生成器

    在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的,就要了解一下迭代器相关的知识了. 迭代器 迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身:next()方法返回容器的下一个元素,在结尾时引发StopIteration异常. __iter__()和next()

  • Python 中迭代器与生成器实例详解

    Python 中迭代器与生成器实例详解 本文通过针对不同应用场景及其解决方案的方式,总结了Python中迭代器与生成器的一些相关知识,具体如下: 1.手动遍历迭代器 应用场景:想遍历一个可迭代对象中的所有元素,但是不想用for循环 解决方案:使用next()函数,并捕获StopIteration异常 def manual_iter(): with open('/etc/passwd') as f: try: while True: line=next(f) if line is None: br

  • 详解Python的迭代器、生成器以及相关的itertools包

    对数学家来说,Python这门语言有着很多吸引他们的地方.举几个例子:对于tuple.lists以及sets等容器的支持,使用与传统数学类似的符号标记方式,还有列表推导式这样与数学中集合推导式和集的结构式(set-builder notation)很相似的语法结构. 另外一些很吸引数学爱好者的特性是Python中的iterator(迭代器).generator(生成器)以及相关的itertools包.这些工具帮助人们能够很轻松的写出处理诸如无穷序列(infinite sequence).随机过程

  • python迭代器与生成器详解

    例子 老规矩,先上一个代码: def add(s, x): return s + x def gen(): for i in range(4): yield i base = gen() for n in [1, 10]: base = (add(i, n) for i in base) print list(base) 这个东西输出可以脑补一下, 结果是[20,21,22,23], 而不是[10, 11, 12, 13]. 当时纠结了半天,一直没搞懂,后来齐老师稍微指点了一下, 突然想明白了-

随机推荐