python -- 生成器 generator
生成器 generator
列表生成式
列表生成式可以使代码更简洁。在Python中,一遍循环一边计算的机制,称为生成器:generator。
# a变量是一个列表
a = [1, 2, 3]
# b变量使用列表生成器生成了一个列表
# 列表生成器的好处,可以动态地生成列表
b = [i*2 for i in range(5)]
'''
如果a也要b的结果就需要这样写,代码变长了。
a = []
for i in range(5):
a.append(i*2)
'''
# <class 'list'> [1, 2, 3]
print(type(a), a)
# <class 'list'> [0, 2, 4, 6, 8]
print(type(b), b)
# a变量是一个列表
a = [1, 2, 3]
# b变量使用列表生成器生成了一个列表
# 列表生成器的好处,可以动态地生成列表
b = [i*2 for i in range(5)]
'''
如果a也要b的结果就需要这样写,代码变长了。
a = []
for i in range(5):
a.append(i*2)
'''
# <class 'list'> [1, 2, 3]
print(type(a), a)
# <class 'list'> [0, 2, 4, 6, 8]
print(type(b), b)
# a变量是一个列表
a = [1, 2, 3]
# b变量就使用列表生成器
# 列表生成器的好处是动态生成列表
b = [i*2 for i in range(500)]
# c变成了一个生成器,返回一个内存地址,c不能像list列表一样去切片。因为数据并非生成。
c = (i*2 for i in range(500))
# <class 'generator'> <generator object <genexpr> at 0x00000000022A2E60>
print(type(c), c)
# 返回一个内存分配对象
print(id(a))
print(id(b))
print(id(c))
# a变量是一个列表
a = [1, 2, 3]
# b变量就使用列表生成器
# 列表生成器的好处是动态生成列表
b = [i*2 for i in range(500)]
# c变成了一个生成器,返回一个内存地址,c不能像list列表一样去切片。因为数据并非生成。
c = (i*2 for i in range(500))
# <class 'generator'> <generator object <genexpr> at 0x00000000022A2E60>
print(type(c), c)
# 返回一个内存分配对象
print(id(a))
print(id(b))
print(id(c))
生成器只有在调用时,才会生出相应的数据。
生成器只有next方法,且用循环的方式调用
a = (i*2 for i in range(500))
# 0
print(a.__next__())
# <method-wrapper '__next__' of generator object at 0x0000000001F22E60>
print(a.__next__)
for i in range(5):
print(a.__next__())
'''
以下是程序的效果:
可以看到最后的for循环是接着之前的a.__next__()执行。
0
<method-wrapper '__next__' of generator object at 0x00000000007D2E60>
2
4
6
8
10
'''
a = (i*2 for i in range(500))
# 0
print(a.__next__())
# <method-wrapper '__next__' of generator object at 0x0000000001F22E60>
print(a.__next__)
for i in range(5):
print(a.__next__())
'''
以下是程序的效果:
可以看到最后的for循环是接着之前的a.__next__()执行。
0
<method-wrapper '__next__' of generator object at 0x00000000007D2E60>
2
4
6
8
10
'''
使用函数生成一个生成器 Step 1
著名的菲波拉契数列,除第一个和第二个数外,任意一个数都由前两个数相加而得。
1, 1, 2, 3, 5, 8, 13, 21, 34, ... ...
# 定义一个fib的函数
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n += 1
fib(10)
# a, b = b, a + b解释
# 相当于:
# t = (b, a+b) t是一个tuple元组
# a = t[0]
# b = t[1]
'''
以下是程序的结果:
1
1
2
3
5
8
13
21
34
55
'''
# 定义一个fib的函数
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n += 1
fib(10)
# a, b = b, a + b解释
# 相当于:
# t = (b, a+b) t是一个tuple元组
# a = t[0]
# b = t[1]
'''
以下是程序的结果:
1
1
2
3
5
8
13
21
34
55
'''
使用函数生成一个生成器 Step 2 -the End
# 定义一个fib的函数
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
# 此时fib()就是一个生成器
# <generator object fib at 0x0000000002822E60>
print(fib(10))
# <generator object fib at 0x0000000002822E60>
print(fib(20))
'''
以下是程序的结果:
<generator object fib at 0x0000000001F12E60>
<generator object fib at 0x0000000002822E60>
'''
# 定义一个fib的函数
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
# 此时fib()就是一个生成器
# <generator object fib at 0x0000000002822E60>
print(fib(10))
# <generator object fib at 0x0000000002822E60>
print(fib(20))
'''
以下是程序的结果:
<generator object fib at 0x0000000001F12E60>
<generator object fib at 0x0000000002822E60>
'''
函数式生成器的用法
使用了函数生成器后,函数可以中断,再执行别的代码,然后再回到函数中断前的地方继续执行。
# 定义一个fib的函数生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
return "Done"
f = fib(6)
# 使用了函数生成器后,函数可以中断,再执行别的代码,然后再回到函数中断前的地方继续执行
print('----------') # black
print(f.__next__())
print(f.__next__())
print('\033[31;1m----------\033[0m') # red
print(f.__next__())
print(f.__next__())
print('\033[32;1m----------\033[0m') # green
print(f.__next__())
print(f.__next__())
print('\033[33;1m----------\033[0m') #yellow
# 定义一个fib的函数生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
return "Done"
f = fib(6)
# 使用了函数生成器后,函数可以中断,再执行别的代码,然后再回到函数中断前的地方继续执行
print('----------') # black
print(f.__next__())
print(f.__next__())
print('\033[31;1m----------\033[0m') # red
print(f.__next__())
print(f.__next__())
print('\033[32;1m----------\033[0m') # green
print(f.__next__())
print(f.__next__())
print('\033[33;1m----------\033[0m') #yellow
使用函数生成一个生成器,函数中return的作用。生成器异常时,打印的消息。
# 定义一个fib的函数生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
return "Done"
f = fib(4)
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
'''
以下是程序的效果:
StopIteration: Done
1
1
2
3
'''
# 定义一个fib的函数生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
return "Done"
f = fib(4)
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
'''
以下是程序的效果:
StopIteration: Done
1
1
2
3
'''
避免生成器溢出后的报错处理,抓报错信息while [ try, except ]。
# 定义一个fib的函数生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
return "Done"
f = fib(10)
while True:
try:
x = next(f)
print(x)
except StopIteration as e:
print(e.value) # 同 print(e)
break
# 定义一个fib的函数生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield b # 这样就是一个生成器的写法
a, b = b, a + b
n += 1
return "Done"
f = fib(10)
while True:
try:
x = next(f)
print(x)
except StopIteration as e:
print(e.value) # 同 print(e)
break
用yield模拟单线程并发效果。又称,协程,比线程要小的单位,协程寄生在线程里。
Part 1 - 2
import time
def consumer(name):
print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
while True:
# yield是保存当前位置,并返回出生成器,便于回来
baozi = yield
print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))
baozi_1 = '韭菜馅'
# 这句话不会有任何反应
c = consumer('Gao')
# 开始执行第一句print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 执行第二句的print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 这里用c.send('str')把baozi_1的内容传给了第二句的print()
# 包子韭菜馅来了,被Gao吃了!
# send是将变量传给yield的位置,以便程序生成器继续
c.send(baozi_1)
def producer(name):
pass
'''
yield保存当前状态并返回
__next__()只是唤醒生成器
send('str')是唤醒生成器,并将str传给yield
'''
import time
def consumer(name):
print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
while True:
# yield是保存当前位置,并返回出生成器,便于回来
baozi = yield
print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))
baozi_1 = '韭菜馅'
# 这句话不会有任何反应
c = consumer('Gao')
# 开始执行第一句print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 执行第二句的print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 这里用c.send('str')把baozi_1的内容传给了第二句的print()
# 包子韭菜馅来了,被Gao吃了!
# send是将变量传给yield的位置,以便程序生成器继续
c.send(baozi_1)
def producer(name):
pass
'''
yield保存当前状态并返回
__next__()只是唤醒生成器
send('str')是唤醒生成器,并将str传给yield
'''
Part 2 - 2
import time
def consumer(name):
print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
while True:
# yield是保存当前位置,并返回出生成器,便于回来
baozi = yield
print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))
def producer(name):
# 这一步是将consumer函数变成生成器,其他什么都没做。
c1 = consumer(name)
name_2 = "{0}的兄弟".format(name)
c2 = consumer(name_2)
# 这一步是让consumer这个生成器,走到yield,并返回yield的位置。因为走到yield这里,所以打印了consumer生成器的第一个print()语句
c1.__next__()
c2.__next__()
print("\033[34;1m~~~开始吃包子了~~~\033[0m")
for i in range(5):
time.sleep(2)
print("~~~做好一个包子了~~~")
# 这一步是接受了i的值,并且继续上次yield的位置,继续下去,继续while里面的print()语句。
c1.send(i)
c2.send(i)
producer('Gao')
import time
def consumer(name):
print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
while True:
# yield是保存当前位置,并返回出生成器,便于回来
baozi = yield
print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))
def producer(name):
# 这一步是将consumer函数变成生成器,其他什么都没做。
c1 = consumer(name)
name_2 = "{0}的兄弟".format(name)
c2 = consumer(name_2)
# 这一步是让consumer这个生成器,走到yield,并返回yield的位置。因为走到yield这里,所以打印了consumer生成器的第一个print()语句
c1.__next__()
c2.__next__()
print("\033[34;1m~~~开始吃包子了~~~\033[0m")
for i in range(5):
time.sleep(2)
print("~~~做好一个包子了~~~")
# 这一步是接受了i的值,并且继续上次yield的位置,继续下去,继续while里面的print()语句。
c1.send(i)
c2.send(i)
producer('Gao')
写在后面
- 生成器可以用来省内存。
- 生成器只有一个方法,next()。
- 生成器的__next__()只能往后走,不能回退,只记录当前数值。
- 生成器只有在调用__next__()时,超时范围,就会返回return值,异常。for,while循环不会。
- 函数变成生成器,只要加入yield即可。
- 生成器可以模拟出单线程并发的效果。
- g.__next__()去激活yield,g.send('str')赋值给yield。