迭代器和生成器

1.可迭代对象:在逻辑上它保存了一个序列,在迭代环境中依次返回序列中的一个元素值。

可迭代对象不一定是序列,但是序列一定是可迭代对象

2.迭代协议:.__next__()方法。

  • 任何对象只要实现了迭代协议,则它就是一个迭代器对象
  • 迭代器对象调用.__next__()方法,会得到下一个迭代结果
  • 在一系列迭代之后到达迭代器尾部,若再次调用.__next__()方法,则会触发StopIteration异常
  • 迭代器在Python中是用C语言的速度运行的,因此速度最快
    迭代器

3.Python3提供了一个内置的next()函数,它自动调用迭代器的.__next__()方法。即给定一个迭代器对象xnext(x)等同于x.__next__()
next()函数

4.内置的iter()函数用于从序列、字典、set以及其他可迭代对象中获取迭代器。

  • 对任何迭代器对象iterator,调用iter(iterator)返回它本身
  • 迭代器对象实现了迭代协议
  • 文件对象本身是一个迭代器对象。即文件对象实现了迭代协议,因此打开多个文件会返回同一个文件对象
  • 列表、元组、字典、set、字符串等不适迭代器对象,他们没有实现迭代协议。因此每次调用iter()均返回一个新迭代器对象。他们支持安装多个迭代器,每个迭代器状态不同

    • 在原地修改列表、set、字典时,会实时反映到它们的迭代器上

    iter()

5.文件迭代器:文件对象本身是一个迭代器(这里文件对象要是读打开)。它的.__next__()方法每次调用时,返回文件中的下一行。当到达文件末尾时,.__next__()方法会引发StopIteration异常。

.readline()方法在到达文件末尾时返回空字符串

文件迭代器

6.字典的迭代器:字典的迭代器在迭代环境中,每次迭代返回的是一个键。
字典的迭代器

7.扩展的列表解析表达式: [ x+y for x in 'abc' if x!='a' for y in 'lmn' if y!='l']
其通用结构为:

[ expression for target1 in iterable1 [if condition1]
         for target2 in iterable2 [if condition2]
         ....]

我们总是可以用for循环手动构建列表解析表达式的结果,但是列表解析表达式性能更好

扩展的列表解析

8.for循环、列表解析、in成员关系测试、map()内置函数、sorted()内置函数、zip()内置函数等都是用迭代协议来完成工作

9.常见的迭代函数:

  • map(func,iterable):它将函数func应用于传入的迭代器的每个迭代返回元素,返回一个新的迭代器,函数执行结果作为新迭代器的迭代值

    map()可以用于多个可迭代对象:map(func,[1,2,3],[2,3,4]),其中func(first,second) 的两个参数分别从两个可迭代对象中获取,函数结果作为新迭代器的迭代值

    map函数

  • zip(iterable1,iterable2,...):它组合可迭代对象iterable1iterable2...中的各项,返回一个新的迭代器。新迭代器长度由iterable1iterable2...最短的那个决定。
    zip函数
  • enumerate(iterable,start):返回一个迭代器对象,它迭代结果是每次迭代返回一个(index,value)元组
    enumerate函数
  • filter(func,iterable):返回一个迭代器对象,它的迭代结果得到iterable中部分元素,其中这些元素使得func()函数返回为真
    filter函数
  • reduce(func,iterable,initial):对iterable中每一项成对地运行func,返回最终值

    reduce函数位于functools包内

    reduce函数

  • sorted(iterable,key=None,reverse=False):排序并返回排好的新列表
    sorted函数

  • sum(iterable,start):返回可迭代对象中的累加值
    sum函数

  • any(iterable):只要可迭代对象iterable迭代返回的某个元素为真则返回True

  • all(iterable):只有可迭代对象iterable迭代返回的所有元素为真则返回True
    any函数和all函数

  • max(iterable,key=func):返回最大元素。若指定func,则返回是func(num)最大的那个元素

  • min(iterable,key=func):返回最小元素。若指定func,则返回是func(num)最小的那个元素
    max函数和min函数

10.set解析、字典解析支持列表解析的扩展语法
扩展解析语法

11.Python3中,range对象不支持.__next__(),因此它本身不是迭代器,而mapzipfilter对象都是迭代器。

  • range不直接生成列表的优点是节约内存空间
  • 由于mapzipfilter对象都是迭代器,因此不支持在它们身上安装多个活跃的迭代器。对他们调用iter()其实返回的是它们本身。
    range对象

12.字典的视图:键视图、值视图、字典视图都没有.__next__()方法,因此他们都不是迭代器
字典的视图都不是迭代器

13.通常列表解析执行速度最快,map()速度次之,for循环速度最慢。前两者以C语言速度执行、后者在Python虚拟机上执行

14.生成器函数:编写为常规的def语句,但是用yield语句一次返回一个结果。每次使用生成器函数时会继续上一轮的状态。

生成器函数会保存上次执行的状态

定义生成器函数的语法为:

    def genFunc(num):
        for i in range(num):
            yield i**2
  • 生成器函数执行时,得到一个生成器对象,它yield一个值,而不是返回一个值。
    • 生成器对象自动实现迭代协议,它有一个.__next__()方法
    • 对生成器对象调用.__next__()方法会继续生成器函数的运行到下一个yield 结果或引发一个StopIteration异常
  • yield语句会挂起生成器函数并向调用者发送一个值。当下一轮继续时,函数会在上一个yield表达式返回后继续执行,其本地变量根据上一轮保持的状态继续使用
  • 生成器函数运行代码随时间产生一系列的值,而不是一次性计算它们。这会节约内存并允许计算时间分散。
    生成器函数

15.for循环(及其它迭代环境)通过重复调用.__next__()方法直到捕获一个异常。若一个不支持迭代协议的对象想用工作在这种环境中,for循环会尝试使用索引协议迭代。

16.生成器对象有一个.send(arg)方法。该方法会将arg参数发送给生成器作为yield表达式的返回值,同时生成器会触发生成动作(相当于调用了一次.__next__()方法。

yield表达式的返回值和生成值是不同的。
返回值是用于生成器函数内部,yield表达式默认返回值为None
而生成值是用于生成器函数外部的迭代返回。

  • 生成器对象必须先启动。启动意味着它第一次运行到yield之前挂起
    启动生成器
  • 要想启动生成器,可以直接使用next(generatorable)函数,也可以使用generatorable.send(None)方法,或者 generatorable.__next__()方法

    next(generatorable)函数相当于使用generatorable.send(None)方法

  • generatorable.send(None)方法会在传递yield表达式的值(默认为None返回值),下一轮迭代从yield表达式返回开始

    每一轮挂起时,yield表达式 yield 一个数,但是并没有返回(挂起了该yield表达式)

    .send()方法

17.生成器表达式:类似于列表解析,但是它是在圆括号中的,而不是方括号中的。

  • 生成器表达式返回的是一个生成器对象
  • 列表解析的结果等同于list()内参数为一个生成器表达式
  • 当生成器表达式在其他括号之内时,它本身的圆括号可以不写
  • 同样的迭代可以用生成器函数或者一个生成器表达式
    • 生成器函数可以包含更多的逻辑
    • 生成器表达式更简洁,没有函数调用产生生成器的过程
    • 生成器函数与生成器表达式支持自动迭代与手动迭代
  • 生成器对象本身是迭代器,因此只支持一个活跃的迭代器。
    生成器表达式

18.生成器函数可以有return,它可以出现在函数内任何地方。生成器函数内遇到return则触发StopIteration异常,同时return的值作为异常说明
生成器函数的return

19.可以调用生成器对象的.close()方法强制关闭它。这样再次给它send()任何信息,都会抛出StopIteration异常,表明没有什么可以生成的了
生成器的close()方法

20.yield from:从PEP 380引入的新特性。

  • yield from可以将一个大的生成器切分成小生成器:

    def generator(): #该生成器yield数字[0~19]
      for i in range(10):
          yield i
      for j in range(10,20):
          yield j
    

    如果你想切分成两个迭代器,可以这么做:

    def generator2():
      for i in range(10):
          yield i
    def generator3():
      for j in range(10):
          yield j
    def generator():
      for i in generator2():
          yield i
      for j in generator3():
          yield j
    

    引入yield from之后你可以这么做:

    def generator2():
      for i in range(10):
          yield i
    def generator3():
      for j in range(10):
          yield j
    def generator():
      yield from generator2()
      yield from generator3()
    
  • yield from能实现代理生成器:

    def generator():
      inner_gen=generator2()
      yield from inner_gen #为了便于说明,这里分两行写
    gen=generator()
    
    • inner_gen迭代产生的每个值都直接作为gen yield值
    • 所有gen.send(val)发送到gen的值val都会被直接传递给inner_gen
    • inner_gen抛出异常:
      • 如果inner_gen产生了StopIteration异常, 则gen会继续执行yield from之后的语句
      • 如果对inner_gen产生了非StopIteration异常,则传导至gen中, 导致gen在执行yield from的时候抛出异常
    • gen抛出异常:
      • 如果gen产生了除GeneratorExit以外的异常,则该异常直接 throw 到inner_gen
      • 如果gen产生了GeneratorExit异常,或者gen.close()方法被调用, 则inner_gen.close()方法被调用。
    • genyield from表达式求职结果是inner_gen迭代结束时抛出的StopIteration异常的第一个参数
    • inner_gen中的return xxx语句实际上会抛出一个StopIteration(xxx)异常, 所以inner_gen中的return值会成为gen中的yield from表达式的返回值。