【Python那些事儿】Python中的生成器
生成器(generator)生成器,即生成一个容器。在Python中,一边循环,一边计算的机制,称为生成器。生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己的内置iter()方法或__iter__()的内置函数),所以,生成器就是一个可迭代对象。在Python中,使用生成器可以很方便的支持迭代器协议。生成器优点python使用生成器对延迟操作提供了支
·
生成器(generator)
- 生成器,即生成一个容器。
- 在Python中,一边循环,一边计算的机制,称为生成器。
- 生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己的内置
iter()
方法或__iter__()的内置函数
),所以,生成器就是一个可迭代对象。 - 在Python中,使用生成器可以很方便的支持迭代器协议。
生成器优点
python使用生成器对延迟操作提供了支持,所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。
- 生成器是可迭代对象;
- 实现了延迟计算,省内存(按需执行);
- 生成器本质和其他类型一样,都是实现了迭代器协议,只不过生成器是一边计算,一边生成,从而节省内存空间,其余的可迭代对象可没有这个功能。
生成器分类
- 生成器函数:生成器函数可以通过常规的def语句定义,不同的是,不是使用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继承它们的状态,来自动实现迭代协议。
- 生成器表达式:类似于列表推导,但,生成器返回按需生产结果的一个对象,而不是一次构建一个结果列表。
生成器表达式
- 生成器表达式:
gen_exp = (i for i in range(10)) #生成器表达式
print(gen_exp) #generator
# for i in gen_exp: #取出生成器表达式的值,for循环
# print(i)
print(gen_exp.__next__()) #next方法
print(gen_exp.__next__())
print(gen_exp.__next__())
print(gen_exp.__next__())
print(gen_exp.__next__())
- 生成器表达式和列表生成式比较:
g = (i for i in range(10**100))#生成器表达式
l = [i for i in range(10**100)]#列表生成式
print g.__next__()#更省内存,需要一个取一个
print l.__next__()#需要在内存中创建1行10**100列的序列
- 列表解析与生成器表达式都是一种便利的编程方式,生成器表达式使用了”惰性计算”(lazy evaluation),只有在检索时才被赋值(evaluated),所以在列表比较长的情况下使用内存上更有效。
- python不但使用迭代器协议让for循环更加通用,大部分内置函数,也是使用迭代器协议访问对象的如,sum函数是python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议。
- 生成器表达式并不是创建一个列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目”产生”(yield)出来。
生成器函数
- 在Python中,使用了yield的函数就称为生成器。
- 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,可以理解为:生成器就是一个迭代器。
- 生成器和函数的执行流程不一样,函数是顺序执行,遇到return语句或者最后一行函数语句就结束。在调用生成器运行过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield值。并在下一次执行next方法时,从当前位置继续运行。
>>> gen = (i for i in range(5))
>>> gen
<generator object <genexpr> at 0x0000004DE29A70A0>
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
3
>>> next(gen)
4
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
注意:generator保存的是算法,每次调用next方法,就计算出gen的下一个元素的值,直到计算到最后一个元素,没有更多元素时,就StopIteration的错误。
当然,上面这种不断调用next(gen),用着有点坑,正确的方法是使用for循环,因为generator也是iterator:
>>> g = (i for i in range(5))
>>> for i in g:
... print(i)
...
0
1
2
3
4
小例子:
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。
更多推荐
已为社区贡献2条内容
所有评论(0)