什么时生成器
通过列表生成式,我们可以直接创建一个列表。 但是, 受到内存的限制,列表的容量是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面的几个元素,那后面绝大多说元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法算出来,那么我们是否可以在循环的过程中不断推算出后续的元素呢? 这样就不必创建完整的list, 从而节省大量的空间。
在python中,这种一边循环一边计算的机制,称为生成器:generator
创建生成器的方法
1. 只要把一个列表生成式的[]改成()
可以通过 next() 函数获得生成器的下一个返回值
生成器保存的是算法, 每次调用next(G),就计算出G的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIeration异常。
当然这种不断调用next()方法太变态了, 所以,我们使用for循环,因为生成器也是可迭代对象。
yield
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。
def fib(times):
n = 0
a, b = 0, 1
while n<times:
print(b)
a, b = b, a+b
n += 1
return 'done'
fib(5)
fib函数实际上是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素, 这种逻辑其实非常类似genertor。
只要把fib函数变成generator,只需要把print(b)改成yield b。
def fib(times):
n = 0
a, b = 0, 1
while n<times:
yield b
a, b = b, a+b
n += 1
return 'done'
F = fib(5)
next(F)
在上面fib 的例子,在循环过程中不断调用 yield ,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fib(5) 不会执行 fib 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fib 函数内部的代码,执行到 yield b 时,fib 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。
send
def gen():
i = 0
while i<5:
temp = yield i
print(temp)
i += 1
f = gen()
next(f)
在代码中, 执行到yield时, gen函数作用暂时保存,返回i的值;temp接收下次.send('python'), send发送的过来的值,c.next()等价c.send(None)
send 主要是反向想函数中传递一个值
例如:
def demo():
n = 10
while n < 20:
temp = yield n
if temp == 1:
print("查询了一条语句")
elif temp == 2:
print("更新了一条语句")
elif temp == 3:
print("删除了一条语句")
n += 1
res = demo()
res.next()
res.send(1)
输出: 查询了一条语句