Python 迭代器&生成器

生成器

  • 通过列表生成式, 可以直接生成一个列表. 但是受到内存限制, 列表容量是有限的. 如果创建一个包含100万个元素的列表, 却只需要访问其中几个, 空间就浪费了.
  • 如果列表元素可以按照某种算法算出来, 在循环的过程中不断推算出后续的元素, 这样就不必创建完整的list,从而节省大量空间. 在Python中, 这种一边循环一边计算的机制, 称为生成器: generator.

创建一个generator, 有很多种方法

  • 第一种方法: 把一个列表生成式的 [ ] 改成 (), 就创建了一个generator
    • 1 >>> L = [x*x for x in range(10)]
      2 >>> L
      3 
      4 >>> g = (x*x for x in range(10))
      5 >>> g
    •  1 #!/usr/bin/python
       2 # -*- coding: utf-8 -*-
       3 
       4 a = []
       5 for i in range(10):
       6 a.append(i*2)
       7 
       8 print(a)
       9 
      10 
      11 print("========== 上述代码等效于下面这句 ========== ")
      12 b = [i*2 for i in range(10)]
      13 print(b)
      14 
      15 
      16 print("============== 更进阶写法 ============== ")
      17 c = [func(i) for i in range(10)]
      18 print(c)
      View Code
    • 注: 其实生成器产没有直接把数据都计算出来, 只是生成了一个地址, 这个地址包含的是计算的方法(下图方法2).调用的时候才会开始产生值.
    •  生成器只有调用的时候才会生成一个个的值, 所以直接像列表一样取某个位置上的值是不行的. 所以生成器是不支持列表的切片等值的.只能通过循环取出具体的值.

    • 生成器只记住当前的位置, 是没有办法往回取值的(比如打了两个__next__()之后想取前边第一个值,是不行的).只能取下一个值.
  • 第二种方法, 用一个函数做生成器
    • 斐波那契数列 (要在哪里返回数据,就在哪里用yield. yield保存了函数中断的点,一旦调用__next__方法, 函数又会从中断点开始继续执行)
      •  1 # !/user/bin/python
         2 # -*- coding: utf-8 -*-
         3 
         4 def fib(max):
         5     n,a,b = 0,0.1
         6     while n<max:
         7         print(b)
         8         a,b = b, a+b
         9         n = n + 1
        10     return 'done'
        11 
        12 fib(10)
        13 
        14 
        15 
        16 # 生成器
        17 def fib(max):
        18     n,a,b = 0,0.1
        19     while n<max:
        20         yield b
        21         a,b = b, a+b
        22         n = n + 1
        23     return 'done'
        24 
        25 print(fib(10))  # 因为fib()是个生成器, 这样只能打印出一个地址. 要想打印出数值,需要用循环,或者___next___()
        26 
        27 f=fib(100)
        28 print(f.__next__())   # 这种方式 ,可以在循环中断在某个地方做些其它的事再回来.
        29 print('============')
        30 print(f.__next__())
        31 
        32 print('======================== loop ========================')
        33 for i in f:
        34     print(i)  # 不会从头开始打印,因为前面有两个print(f.__next__())
        View Code
      • 抓取异常
      •  1 # !/user/bin/python
         2 # -*- coding: utf-8 -*-
         3 
         4 def fib(max):
         5     n,a,b = 0,0.1
         6     while n<max:
         7         print(b)
         8         a,b = b, a+b
         9         n = n + 1
        10     return 'done'
        11 
        12 fib(10)
        13 
        14 
        15 
        16 # 生成器
        17 def fib(max):
        18     n,a,b = 0,0.1
        19     while n<max:
        20         yield b
        21         a,b = b, a+b
        22         n = n + 1
        23     return 'done'
        24 
        25 print(fib(10))  # 因为fib()是个生成器, 这样只能打印出一个地址. 要想打印出数值,需要用循环,或者___next___()
        26 
        27 f=fib(100)
        28 print(f.__next__())   # 这种方式 ,可以在循环中断在某个地方做些其它的事再回来.
        29 print('============')
        30 print(f.__next__())
        31 
        32 print('======================== loop ========================')
        33 for i in f:
        34     print(i)  # 不会从头开始打印,因为前面有两个print(f.__next__())
        35 
        36 
        37 #  抓取异常
        38 f=fib(10)
        39 g=fib(6)
        40 while True:
        41     try:
        42         x = next(g)
        43         print('g: ', x)
        44     except StopIterration as e:
        45         print('Generator return  value: ', e.value)
        46         break
        View Code

 生成器并行运算

 1 # !/user/bin/python
 2 # -*- coding: utf-8 -*-
 3 import time
 4 
 5 def consumer(name):
 6     print('%s 准备吃包子了' %name)
 7     while True:
 8         baozi = yield
 9         print('包子[%s] 来了, 被[%s]吃了' %(baozi, name))
10 
11 
12 c = consumer('lucy')
13 c.__next__()  # 输出第一个print的内容  print('%s 准备吃包子了' %name)
14 c.__next__()  # 输出第二个print的内容, print('包子[%s] 来了, 被[%s]吃了' %(baozi, name))
15 b1 = '韭菜'
16 c.send(b1)  # 把包子的参数传过去,被yield接收到,传给baozi.  # 返回包子[韭菜] 来了, 被[lucy]吃了
17 
18 '''
19 send和next的区别: send唤醒生成器继续打印,并且会把参数传给yield; next只能唤醒生成器, 不能传值.
20 '''
21 
22 # 单线程下的并行交换, 也叫携程.
23 
24 def producer(name):
25     c = consumer('A')
26     c2 = consumer('B')
27     c.__next__()
28     c2.__next__()
29     print('老子开始准备做包子了')
30     for i in range(10):
31         time.sleep(1)
32         print('做了1个包子,分两半')
33         c.send(i)
34         c2.send(i)
35 
36 
37 producer('alex')

迭代器

可直接作用于for循环的数据类型有以下几种:

a. 一类是集合数据类型, 如list, tuple, dict, set, str等

b. 一类是generator, 包括生成器和带yield的generator function 

这些可以直接作用于for循环的对象统称为可迭代对象: Iterable.

可以用例isinstance()判断一个对象是否是Iterable对象.

1 from collections import Iterable
2 
3 isinstance([],Iterable)
4 isinstance({ },Iterable)
5 isinstance('AB',Iterable)
6 isinstance((x for x in range(10)), Iterable)
7 isinstance(100, Iterable)

生成器不但可以作用于For循环, 还可以被next() 函数不断调用 并返回下一个值, 直到最后抛出StopIteration错误表示无法继续返回下一个值了.

可以被next()函数调用并不断返回下一个值的对象称为换代器: Iterator.

可以使用isInstance()判断一个对象是否是Iterator对象:

 1 from collections import Iterable
 2 
 3 isinstance([],Iterable)  # True
 4 isinstance({ },Iterable)  # True
 5 isinstance('AB',Iterable)  # True
 6 isinstance((x for x in range(10)), Iterable)  # True
 7 isinstance(100, Iterable)  # True
 8 
 9 
10 from collections import Iterator  # (没有__next()__方法就不是迭代器):
11 isinstance((x for x in range(10)), Iterator)  # True
12 isinstance([], Iterator)  # False
13 isinstance((), Iterator)  # False
14 isinstance('ab', Iterator)  # False
15 
16 
17 '''
18 生成器都是Iterator对象, 但list, dict, str是Iterable, 却不是Iterator.
19  把list, dict, str等Iterable变成Iterator可以使用iter()函数
20 '''

猜你喜欢

转载自www.cnblogs.com/cheese320/p/8968983.html