python生成器
生成器的使用场景:
通常创建了一个数据量很大的,如一百万个元素的数据,需要很大的存储空间,而我们通常仅仅需要其中的几百个或几个,而其他用不上的数据则比较占用空间,这时就需要用到生成器
- 生成器是特殊的迭代器
- 生成器只存储算法,不存储数据
- 生成器是一边循环一边执行的计算机制
- 主要作用:节省内存,提高算法效率
生成器的第一种写法(简易版)
g = (x for x in range(10))
从collections包中导入Iterable,Iterator
判断g的数据类型
print(isinstance(g,Iterator)) >>>True
print(isinstance(g,Iterable)) >>>True
都返回True,说明生成器既可以迭代,同时也是一种特殊的迭代器
可以通过next()方法唤醒生成器
print(next(g)) >>> 0
print(next(g)) >>> 1
print(next(g)) >>> 2
...
使用next()方法超出范围后,会报错 StopIteration
可用for循环去迭代,因为for循环可以自动捕获StopIteration,然后跳出,不会报错,所以通常使用for循环去迭代生成器
for i in g:
print(i)
第二种生成器写法(用于复杂算法)
使用生成器实现斐波那契数列(兔子问题)
def FB(month):
m = 0
a, b = 0, 1
while m < month:
yield b # yield 是区别函数和生成器的关键字
a, b = b, a + b
m += 1
# 一旦函数中存在有yield关键字,这个函数就是一个生成器
yield的简单理解:
yield是时停版的“return”,既具有return的返回功能,同时附加了时停的替身,每当程序运行到yield的位置时,都会先返回yield后面的值,然后暂停函数,下一次继续调用函数的时候,从yield的后面开始运行
yield生成器函数的取值 注意点
在使用next()或者__next__方式取值的时候,需要注意函数的引用问题,如果不先将函数赋值给其他变量,会导致无论打印多少次出来的值都是同一个值,如下所示:
# 不赋值变量,直接打印数据,所有结果均为1
# 相当于每一次都重新启用FB()函数,然后往里面传值
print(next(FB(9))) >>> 1
print(next(FB(9))) >>> 1
print(next(FB(9))) >>> 1
print(next(FB(9))) >>> 1
print(next(FB(9))) >>> 1
print(next(FB(9))) >>> 1
print(FB(9).__next__()) >>> 1
print(FB(9).__next__()) >>> 1
print(FB(9).__next__()) >>> 1
print(FB(9).__next__()) >>> 1
print(FB(9).__next__()) >>> 1
print(FB(9).__next__()) >>> 1
print(FB(9).__next__()) >>> 1
...
将函数赋值给变量后,再使用next()或__next__方法打印数据
#将FB(9)赋值给变量a后,再打印数据
a = FB(9)
print(next(a)) >>> 1
print(next(a)) >>> 1
print(next(a)) >>> 2
print(next(a)) >>> 3
print(next(a)) >>> 5
print(next(a)) >>> 8
print(next(a)) >>> 13
...
多个yield的用法
def sendTest():
while True:
v = yield 'hello world'
r = yield v
a = yield r
yield a
if __name__ == '__main__':
test = sendTest()
print(test.__next__()) # 返回第一个yield后面的'hello world'
print(test.__next__()) # 第一个yield抛出'hello world'后,为None,赋值给v,第二个yield返回None
print(test.__next__())# 同上
print(test.__next__())# 同上
# 输出结果
>>> hello world
>>> None
>>> None
>>> None
使用yield关键字的函数,配合send()方法,还支持从外部传入参数进去
def sendTest():
while True:
v = yield 'hello world
r = yield v # 当第二个print执行的时候,send传送的‘空条承太郎’会赋值给 v,所以第二个yield抛出‘空条承太郎’后,为None
a = yield r
yield a
if __name__ == '__main__':
test = sendTest()
print(test.__next__())
print(test.send('空条承太郎'))
print(test.__next__())
print(test.__next__())
# 输出结果
>>> hello world
>>> 空条承太郎
>>> None
>>> None
在使用send()方法往里面传参的时候,如果未先使用next()或__next__方法唤醒生成器,则会报错,报错示范如下:
def sendTest():
while True:
v = yield 'hello world
r = yield v
a = yield r
yield a
if __name__ == '__main__':
test = sendTest()
print(test.send('空条承太郎'))
print(test.__next__())
print(test.__next__())
# 输出结果
>>> TypeError: can't send non-None value to a just-started generator