当前位置:
首页 > Python基础教程 >
-
Python学习笔记:生成器(Generator)详解
一、列表生成式:可以动态生成列表,而不是用固定值给列表赋值,这样程序会更灵活
def test(i): # 取偶数 if i % 2 == 0: return i # 普通的生成式 list1 = [i * 2 for i in range(10)] print(list1) # 通过一个函数选择值 list1 = [test(i) for i in range(10)] print(list1)
二、生成器:列表在调用之前已经准备好了,如果这个列表的数据量巨大,那么就一直占用内存并且效率低, 比如有一个100万元素的列表,本次循环只循环到前面100个,后面的元素没有用到,有没有一种 方式可以在调用的时候再生成元素呢?这就是生成器。
# (i * 2 for i in range(10000)) 返回一个生成器的内存地址 list3 = (i * 2 for i in range(10000))
注意:生成器不能用“list[0]”这种方式调用里面的元素,因为此时元素还没有生成,只能用for循环来访问
# 可以用__next()__方法来取得下一个,取得下一个的同时前面一个元素就消失了,所以生成器只记录当前位置的元素。不能往后退,往前用__next()__ print(list3.__next__()) print(list3.__next__()) print(list3.__next__())
# 一般都是用循环来访问,不会用到__next__()方法 # 这种方式就是使用了python内置的迭代器,里面内置有next方法 for i in list3 print(i)
举个例子,将斐波那契数列用生成器来生成,(斐波那契数列定义:除了第一个和第二个数,后面的数都是前面两个数字想加得到)
下面普通写法
list_num = (i for i in range(10)) list_new_num = [] for i in list_num: if i != 0 and i != 1: list_new_num.append(list_new_num[i - 1] + list_new_num[i - 2]) else: list_new_num.append(i) print(list_new_num[i])
斐波那契数列的另外一种写法
n, a, b = 0, 0, 1 # 连续赋值 while n < 10: print(b) a, b = b, a + b n = n + 1
将上面的代码改一下。 通过yield关键词保存函数的中断状态,外面调用的代码可以先执行,不需要等到所有值都计算出来了再执行,这样可以大大提高效率 执行到next()的时候再返回yield的位置继续执行函数内部的代码。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 加上yield就变成一个生成器了,相当于保存了b变量此处的值。这个b是要传到外面的,所以用yield标记
a,b = b,a+b
n = n+1
return "出错了" # 这是错误信息,将会被异常捕获。
# 返回一个生成器,所以fib相当于这个生成器的具体实现。
g = fib(6)
while True:
try:
# 这个next是一个内置方法,作用跟__next()__一样,取出生成器的下一个元素
# next后进入上面fib函数的yield的位置
x = next(g)
print("g:",x)
except StopIteration as e: # 这里捕捉异常来结束程序
print("error:",e.value)
break
yield 还可以用做单线程下的协程的调用,虽然仍然是单线程(线程和协程将在后面单独介绍),但是通过yield能大大提高效率 以下是一个典型的生产消费模型,一个人生产,若干人消费,而且是同时进行,也是利用了yield的特性,我们称为“并行生成器”
import time def consumer(name): print("%s准备吃包子了!"%name) while True: baozi = yield # yield无返回值,为什么赋值给baozhi呢? print("包子[%s]来了,被[%s]吃了。" % (baozi,name)) # c = consumer("tangwei") # c.__next__() # 第一个__next__执行到consumer函数中的yield的位置,然后返回 # c.__next__() # 第二个__next__继续执行剩下的部分一直到循环继续来执行到yield跳出 # c.__next__() # 第一个__next__执行到consumer函数中的yield的位置,然后返回 # c.send("肉馅") # send将一个值传递给consumer函数内的yield的位置,也就是赋值给变量baozi然后next,仅调用__next__不会给yield传递值 def producer(name): # 生成1个生成器 c1 = consumer("tangwei") # 生成1个生成器 c2 = consumer("chenyadan") # c1准备吃包子,__next__执行到consumer函数中的yield的位置,然后返回 c1.__next__() # C2同上 c2.__next__() print("我是厨师[%s],开始做包子了..."%(name)) for i in range(10): time.sleep(1) print("做了一个包子,一人一半..") c1.send(i) # send将一个值传递给consumer函数内的yield的位置,也就是赋值给变量baozi,仅调用__next__不会给yield传递值 c2.send(i) # 虽然程序依然是顺序执行的,但是因为程序流可以在函数之间跳转,就无需等到一个函数结束以后再执行另外一个函数,这样大大提高了执行效率。 # 这是单线程下的并行效果,也就是协程,协程是比线程更小的单位,寄生在线程中。 producer("狗不理")
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式