22、Python深入理解生成器

生成器是python非常独特的特性,在C、Java中完全没有相似或可替代的语法。Python因为GIL的原因,多线程的使用有很大的限制(或性能不佳),所以广泛的要使用协程,而Python协程的基础便是生成器(与goLang的协程不一样),所以在Python中生成器是很多高级语法的基础。

生成器是Python中的一种特殊类型的迭代器,允许你逐个生成值而不需要一次性地在内存中存储所有值。使用yield语句来生成一个值序列,而不是像普通函数那样一次性返回一个完整的列表。

生成器函数是一个使用yield关键字的函数,而不是return。每次调用生成器函数时,它会返回一个值,然后暂停并保存其当前状态,以便下次调用时可以从暂停的位置继续执行。

如何创建生成器?

  1. 生成器表达式:形式和 列表推导式 类似,只不过将中括号 [] 替换成了小括号 ()。
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
  1. 生成器函数
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__()方法中实现所有的控制流逻辑。

迭代器使用场景

  • 遍历文件的每一行,而不需要将整个文件加载到内存中。
  • 实现自定义的数据结构,如链表、树等,并提供迭代功能。
  • 与数据库交互时,逐行获取查询结果,而不是一次性获取所有结果。

生成器使用场景

  • 创建一个无限的数值序列,如斐波那契数列。
  • 实现协程(coroutine),用于编写异步代码。
  • 在处理大数据时,按需生成和处理数据块,以减少内存占用。

send方法

generator.send(value)
  • generator是生成器对象。
  • 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")

你可能感兴趣的:(Python3编程技巧进阶,python,开发语言)