生成器
生成器
为什么需要生成器:
假如现在有一个需求,我要打印从1-1亿的整形。如果我们采用普通的方式,直接调用range
函数,那么程序肯定会崩溃,因为range(1,100000000)
函数直接产生一个从1-1亿的列表,这个列表中的所有数据都是存放在内存中的,会导致内存爆满。这时候我们可以采用生成器来解决这个问题,生成器不会一次性把所有数据都加载到内存中,而是在循环的时候临时生成的,循环一次生成一个,所以在程序运行期间永远都只会生成一个数据,从而大大节省内存。
解决打印1-1亿的问题:
用range
函数配合圆括号可以产生一个生成器:
# 普通的列表 num_list = [x for x in range(1, 100000000)] print(type(num_list)) # 生成式 num_gen = (x for x in range(1, 100000000)) print(type(num_gen)) //<class 'list'> //<class 'generator'>
next
函数和__next__
方法:
next
函数可以迭代生成器的返回值
自己写生成器:
生成器可以通过函数产生。如果在一个函数中出现了yield
表达式,那么这个函数将不再是一个普通的函数,而是一个生成器函数。yield
一次返回一个结果,并且会冻结当前函数的状态。以下是一个非常简单的生成器:
def my_gen(): yield 1 yield 2 yield 3
那么想要获取里面的值,可以通过以下方式获取:
ret = my_gen() print(next(ret)) print(next(ret)) print(next(ret))
send
方法:
send
方法和next
方法类似,可以用来触发生成器的下一个yield
,但是send
不仅可以触发下一个yield
,还可以发送数据过去,作为yield
表达式的值。
def my_gen(start): while start < 10: temp = yield start print(temp) start += 1 ret = my_gen(1) # next(ret)返回1 # 以下结果会打印1 print(next(ret)) # send("Hello World")返回2 # 但是会将"Hello World"赋值给yield start # 因此temp = "Hello World" # 那么会打印"Hello World" # 并且打印2 print(ret.send('Hello World'))
生成器中的return
语句会触发StopIterator
异常:
def my_gen(start): while start < 3: temp = yield start # print(temp) start += 1 # 触发StopIterator异常,会导致不能继续遍历 return "nihao" ret = my_gen(1) for x in ret: print(x)
生成器的使用案例:
- 用
yield
实现斐波拉契数列: 斐波拉契数列的算法:除第一个和第二个数以外,任意一个数都可由前两个数相加得到:1,1,2,3,5,8,13,21,34,55... -
用
yield
做多任务切换:def neteasy_music(duration): c_time = 0 while c_time < duration: print('播放音乐 %d分钟'%c_time) yield None c_time += 1 def youku_movie(duration): c_time = 0 while c_time < duration: print('播放电影 %d分钟' % c_time) yield None c_time += 1 def main(): music = neteasy_music(10) qq = youku_movie(10) music_stop = False movie_stop = False while True: try: next(music) except StopIteration: print('音乐播放完毕') music_stop = True try: next(qq) except StopIteration: print('电影播放完毕') movie_stop = True if music_stop and movie_stop: break
-
用
yield
做一个简单的协程:from urllib.request import urlretrieve def Consumer(url): while True: if url: filename = url.split('/').pop() url = yield urlretrieve(url,filename='images/'+filename) print('consumer') def Producter(consumer): # 先执行一下,让后面的send不会报错 index = 2 next(consumer) while index < 10: print('生产者已经获取了第%d个图片链接' % index) url = 'http://127.0.0.1:9000/%d.jpg' % index consumer.send(url) print('producter') index += 1 consumer.close() con = Consumer("http://127.0.0.1:9000/1.jpg") Producter(con)