生成器是python非常独特的特性,在C、Java中完全没有相似或可替代的语法。Python因为GIL的原因,多线程的使用有很大的限制(或性能不佳),所以广泛的要使用协程,而Python协程的基础便是生成器(与goLang的协程不一样),所以在Python中生成器是很多高级语法的基础。
生成器是Python中的一种特殊类型的迭代器,允许你逐个生成值而不需要一次性地在内存中存储所有值。使用yield语句来生成一个值序列,而不是像普通函数那样一次性返回一个完整的列表。
生成器函数是一个使用yield关键字的函数,而不是return。每次调用生成器函数时,它会返回一个值,然后暂停并保存其当前状态,以便下次调用时可以从暂停的位置继续执行。
generator = (x * x for x in range(8))
print(type(generator)) #
for i in generator:
print(i) # 0,1,4,9,16,25,36,49
def createNum():
for x in range(8):
yield x * x
g = createNum()
print(type(g)) #
for i in g:
print(i) # 0,1,4,9,16,25,36,49
我们会发现生成器的功能和迭代器非常类似,那他们到底有什么区别?
定义方式:迭代器是实现了迭代器协议(即__iter__()和__next__()方法)的对象;生成器则是使用了yield关键字的函数,当这个函数被调用时,它返回一个生成器对象。
状态保存:生成器在每次产生一个值后会自动保存当前的状态,下次调用时会从上次离开的地方继续执行;而迭代器则不会自动保存状态,它依赖于用户显式地请求下一个值。
内存使用:生成器在迭代过程中只会生成当前需要的值,而不是一次性生成所有的值,所以它可以处理大数据集,而不会耗尽内存;而迭代器则可能需要一次性生成所有的值。
重复使用:迭代器可以被多次迭代,每次迭代都会从头开始;而生成器只能被迭代一次,因为它不保存整个序列,只保存当前的状态。
灵活性:生成器更加灵活,可以使用任何种类的控制流语句;而迭代器则需要在__next__()方法中实现所有的控制流逻辑。
迭代器使用场景:
生成器使用场景:
generator.send(value)
send()函数是用于与生成器进行交互的一种方法。send()函数会将值发送给生成器,并将该值作为yield表达式的结果。同时,生成器会从上一次暂停的地方继续执行,并执行到下一个yield语句或函数结束。
当首次调用生成器时,必须使用next()函数来启动生成器,或者使用send(None),将None作为参数发送给生成器。这是因为生成器在第一次调用时需要被激活,以准备接收来自send()函数的值。
coroutine是一个生成器函数,它使用yield关键字来挂起执行并等待外部事件或数据。首次使用next()来激活它,然后通过send()方法向协程发送数据。每次调用send()都会导致协程接收数据,处理它,并再次挂起,直到协程结束或者再次被激活。
from time import sleep
def coroutine():
while True:
# 生成器挂起
received = yield
print(f"Received: {received}")
c = coroutine()
# 首次调用next()激活协程,但不传入值
next(c)
# 向协程发送数据,协程将处理这些数据
c.send("Hello")
sleep(1)
c.send("World")