Python-生成器

1、生成器***

  之前在列表解析中已经介绍了 生成器表达式。这里介绍生成器函数。

  生成器:generator

    生成器指的是生成器对象,可以有生成器表达式得到,可以使用yield 关键字得到一个生成器函数,调用这个函数就得到一个生成器对象。

  生成器函数

    函数体重包含yield语句 的函数,返回生成器对象

    生成器对象,是一个可迭代对象,是一个迭代器。

    生成器对象,是惰性求值的

    举例 1:

 1 def fn():
 2     for i in range(5):
 3         yield i
 4 
 5 print(type(fn())) # <class 'generator'>
 6 
 7 for x in fn():
 8     print(x)
 9 print('-------------------------')    
10 for x in fn():# 这个第二次还能继续循环迭代是因为第二次循环又重新调用了fn(),作为一个生成器对象
11     print(x)
12 print('-------------------------')
13 
14 g = fn() #
15 
16 for x in g:
17     print(x)
18 print('-------------------------')    
19 for x in g: # 还是从之前的生成器中继续迭代
20     print(x)

     举例2:

 1 def fen():
 2     print('line 1')
 3     yield 1
 4     print('line 2')
 5     yield 2
 6     print('line 3')
 7     for i in range(5):
 8         print(i)
 9     return 3
10 g = fen()
11 
12 count = 0
13 for x in g:
14     count += 1
15     print(x,count)
16 
17 # line 1
18 # 1 1
19 # line 2
20 # 2 2
21 # line 3
22 # 0
23 # 1
24 # 2
25 # 3
26 # 4

     总结:

      • 生成器函数中,使用过个yield 与,执行一次后会暂停执行,把yield表达式的值返回
      • 再次执行会执行到下一个yield语句
      • return 语句 依然可以终止函数运行,但return 语句的返回值不能被获取到
      • return 会导致无法继续去哦去下一个值,抛出StopIteration 异常
      • 如果 函数没有显示的return 语句,如果生成器函数执行到结尾一样会抛出StopIteartion异常

     生成器函数总结

      1. 包含yield 语句的生成器函数生成 生成器对象的时候,生成器函数的函数体不会立即执行
      2. next(generator) 会从函数的当前位置向后执行到之后碰到拿到的第一个yield语句,会弹出,并暂停函数执行。
      3. 再次调用next函数,和上一条处理过程一样
      4. 没有多余的yield 语句能被执行,继续调用next函数,会抛出StopIteration

     举例 3:

 1 def counter():
 2     i = 0
 3     while True:
 4         i += 1
 5         yield i
 6 def inc(c):
 7     return next(c)
 8 
 9 c = counter() #每次都要外部传参
10 print(inc(c)) # 1
11 print(inc(c)) # 2
12 
13 def counter():
14     i = 0
15     while True:
16         i += 1
17         yield i
18 def inc():
19     c = counter() # 每次都重新调用,所以每次都拿到同样的值
20     return next(c)
21 
22 print(inc()) # 1
23 print(inc()) # 1
24 print(inc()) # 1

     举例 4:

 1 '''
 2     上面的优化:
 3         1、不需要外部传参,感觉 inc() 像一个生成器对象
 4         2、内层函数作为一个生成器函数,并且在同级进行调用,作为生成器对象
 5         3、外层函数将生成器对象地址返回给 外层函数
 6         4、全局调用外层函数,就相当于是一个生成器对象。
 7 '''
 8 def inc():
 9     def countes():
10         i = 0
11         while True:
12             i += 1
13             yield i
14     c = countes()
15     return lambda :next(c)
16 
17 foo = inc()
18 print((foo())) # 1
19 print((foo())) # 2

     举例 5:

 1 def fib(n):
 2     a = 0
 3     b = 1
 4     for i in range(n):
 5         a, b = b, a + b
 6         yield a
 7 
 8 foo = fib(5)
 9 # for _ in range(5):
10 #     print(next(foo))
11 
12 for i in foo:
13     print(i)
14 
15 
16 
17 def fib():
18     x = 0
19     y = 1
20     while True:
21         yield y
22         x, y = y, x + y
23 
24 foo = fib()
25 for _ in range(5):
26     print(next(foo))
27 
28 print('-------')
29 
30 for _ in range(10):
31     # next(foo)
32     print(next(foo),'--')
33 
34 # 1
35 # 1
36 # 2
37 # 3
38 # 5
39 # -------
40 # 8 --
41 # 13 --
42 # 21 --
43 # 34 --
44 # 55 --
45 # 89 --
46 # 144 --
47 # 233 --
48 # 377 --
49 # 610 --

  生成器应用:yield引出的(遇到yield,会让出控制权)

    • 协程Co-routine
      • 生成器的高级用法
      • 比进程、线程轻量级
      • 是在用户空间调度函数的一种实现
      • Python3 asyncIO 就是协程实现,已经加入标准库
      • Python3.5 使用async,await关键字直接原生支持协程
      • 协程调度器实现思路:
        • 有两个生成器A,B
        • next(A) 后,A 执行到了yield 语句暂停,然后去执行next(B),B执行到yield 语句也暂停,然后再次调用next(A),再调用next(B),周而复始,就实现了调度的效果,
        •  1 # 在用户空间就做了调度
           2 def a():
           3     for i in range(5):
           4         yield i
           5 
           6 def b():
           7     for i in range(5):
           8         yield i
           9 
          10 a = a()
          11 b = b()
          12 
          13 for i in range(10):
          14     if i % 2 == 0:
          15         print(next(a), 'a')
          16     else:
          17         print(next(b), 'b')
          18 # 0 a
          19 # 0 b
          20 # 1 a
          21 # 1 b
          22 # 2 a
          23 # 2 b
          24 # 3 a
          25 # 3 b
          26 # 4 a
          27 # 4 b
          协程mode
        • 可以引入调度的策略来实现切换的方式
      • 协程是一种非抢占式调度
      • 协程效率是比较高的

   yield from:

1 def inc():
2     for x in range(100):
3         yield x
4         
5         ||
6         ||
7         
8 def inc():
9     yield  from range(100)

  yield from 是Python3.3 出现的新的语法

  yield from iteable 是 for item in iterable : yield item 形式的语法糖

    从 可迭代 对象中一个个拿 元素   

 1 def counter(n):
 2     for x in range(n):
 3         yield x
 4 
 5 def inc(n):
 6     yield  from counter(n)
 7 
 8 foo = inc(10)
 9 
10 print(next(foo)) # 0
11 print(next(foo)) # 1

2、send触发yield返回值原理

 1 def test():
 2     print('开始啦')
 3     firt = yield 1      # return 1,first=None
 4     print('第一次', firt)
 5     yield 2
 6     print('第二次')
 7 
 8 t = test()
 9 # print(next(t)) # next(t)
10 # print('--------------')
11 # print(next(t)) # next(t)
12 # print(next(t,'end')) # next(t)
13 res=t.send(None) # 第一次进入后,没有接受值的 标识符,所以传入None
14 
15 print(res)
16 
17 res=t.send(10)
18 print(res)
为什么要坚持,想一想当初!
原文地址:https://www.cnblogs.com/JerryZao/p/9533134.html