17. 生成器

Posted 村里唯一的运维

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了17. 生成器相关的知识,希望对你有一定的参考价值。

生成器

1. 概述

元组推导式是生成器(generator)
生成器本质是迭代器,允许自定义逻辑的迭代器

迭代器和生成器区别:
	迭代器本身是系统内置的.重写不了.而生成器是用户自定义的,可以重写迭代逻辑

生成器可以用两种方式创建:
    (1)生成器表达式  (里面是推导式,外面用圆括号)
    (2)生成器函数    (用def定义,里面含有yield)

2. 生成器的基本用法

生成器表达式

gen = ( i for i in range(5))
print(gen,type(gen))

生成器中获取数据的方式

for循环

gen = ( i for i in range(5))
for i in gen:
	print(i)

next调用

gen = ( i for i in range(5))
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)

for循环和next配合调用生成器中的数据

gen = ( i for i in range(5))
for i in range(5):
	res = next(gen)
	print(res)

3. 生成器函数

yield 类似于 return
共同点在于:执行到这句话都会把值返回出去
不同点在于:yield每次返回时,会记住上次离开时执行的位置 , 下次在调用生成器 , 会从上次执行的位置往下走
		   而return直接终止函数,每次重头调用.

生成器函数基本用法

def mygen():
	print("one")
	yield 1	
	print("two")
	yield 2	
	print("three")
	yield 3
    
初始化生成器函数 返回生成器对象, 简称生成器
gen = mygen()
print(isinstance(gen,Iterator))
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
# res = next(gen) error 越界错误
# print(res)

上述代码解析

代码解析:
通过next调用生成器对象,找到mygen函数,代码依次从上到下执行:
先走12行 print("one")  yield 1 记录当前执行代码的位置状态 , 添加阻塞 ,把1直接返回,等待下一次调用

在通过next进行调用时,
从上一次记录的那个位置状态往下走,

print("two") yield 2 记录当前执行代码的位置状态 , 添加阻塞 ,把2直接返回,等待下一次调用

在通过next进行调用时,
从上一次记录的那个位置状态往下走,

print("three") yield 3 记录当前执行代码的位置状态 , 添加阻塞 ,把3直接返回,等待下一次调用

在通过next进行调用时,
从上一次记录的那个位置状态往下走,发现已经没有yield,直接越界报错.

send

不但可以获取值,还能发送值,发送给上一个yield

next和send区别:
	next 只能取值
	send 不但能取值,还能发送值
send注意点:
	第一个 send 不能给 yield 传值 默认只能写None
	最后一个yield 接收不到send的发送值

示例说明

def mygen():
	print("program start")
	res = yield 1
	print(res)
	res = yield 2
	print(res)
	res = yield 3
	print(res)
	print("program end")
# 初始化生成器函数 => 变成生成器对象 简称 生成器
print("<===================>")
gen = mygen()
res2 = gen.send(None)
print(res2)
res2 = gen.send(1111)
print(res2)
res2 = gen.send(2222)
print(res2)


代码解析

代码解析:
res = gen.send(None) 第一次通过send 调用时,因为send 默认发送给上一个yield,所以第一次发送不了,只能默认发送None,属于硬性语法
第一次代码调用时,只走到了3行 ,记录代码执行的位置状态,添加阻塞,返回值1

在调用时,通过send(1111)给yield 1 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走4行代码,打印 1111
继续执行5行代码, 把yield 2返回,记录代码执行的位置状态,添加阻塞,返回值2

在调用时,通过send(2222)给yield 2 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走6行代码,打印 2222
继续执行7行代码, 把yield 3返回,记录代码执行的位置状态,添加阻塞,返回值3

在调用时,通过send(3333)给yield 3 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走85行代码,打印 3333
问题是再也没有yield 响应的返回了,直接报错.

yield from : 将一个可迭代对象变成一个迭代器返回

系统默认会把from后面的可迭代性数据,每个元素扔到迭代器中,通过next一个一个调用

def mygen():
	yield from ["张三","李四","王五","大刀"]
gen = mygen()
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)

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

 


以上是关于17. 生成器的主要内容,如果未能解决你的问题,请参考以下文章

生成器yield(17-06)

17. Python 生成式   生成器   迭代器

1.17 Python基础知识 - 迭代器和生成器初识

python-17

AD17 PCB保存失败 同时生成一个紧急备份文件

AD17 PCB保存失败 同时生成一个紧急备份文件