一、概述

  • 生成器是一种在 Python 中的迭代器生成器。生成器是一个函数,它生成一个迭代器。当生成器函数被调用时,它不会当即履行,而是回来一个生成器目标,该目标能够被用于迭代。生成器能够利用 yield 句子在函数内部生成值,并在函数调用者处接纳这些值。

  • 协程是一种高效的、内存友爱的、线程内的并发技能,它能够让您在单个线程内并发地履行多个使命。协程是经过运用 async 关键字完成的,并能够在 Python 中的 asyncio 库中运用。与线程不同,协程不需求额外的体系线程,因而它们比线程更高效、更灵敏。

在简略的说法,生成器用于生成一系列的值,而协程用于在单个线程中并发履行多个使命。

Python 高级编程之生成器与协程进阶(五)

二、生成器

生成器表达式本质上便是一个迭代器,是界说迭代器的一种办法,是答应自界说逻辑的迭代器。生成器运用generator表明。

  • 生成器能够运用 for 循环或 next() 函数来遍历。当生成器目标被创立时,它会保存函数的当时状况,并在每次调用 next() 或 for 循环时从当时状况开端履行,直到遇到 yield 句子停止。

  • 生成器遇到 yield 句子时,它会生成当时的值,并保存函数的当时状况,以便下次调用时能够从该状况开端持续履行。当生成器再次被调用时,它会持续履行从上次暂停的当地开端,直到遇到下一个 yield 或许 return 句子,或许函数完毕停止。

下面是一个生成器函数的示例:

def my_generator():
    for i in range(3):
        yield i
gen = my_generator()
for i in gen:
    print(i)

输出:

0
1
2

从上面的示例能够看出,生成器的工作原理是经过保存函数的当时状况,以便每次调用时从当时状况开端持续履行,并运用 yield 句子生成值的。

1)生成器和迭代器的差异

生成器和迭代器是 Python 中的两个相关的概念,可是有一些差异:

  • 界说生成器是一种特别的迭代器,它能够生成一系列的值,而迭代器是一个目标,它完成了 iternext 办法,能够回来一个值的序列。

  • 创立:生成器能够经过界说生成器函数,在函数内部运用 yield 句子生成值;迭代器能够经过界说迭代器类,在类中完成 iternext 办法。

  • 功率:生成器函数在生成值时只需求暂停函数的履行,因而它具有更高的功率;迭代器类需求保护一个目标状况,因而功率较低。

  • 用处:生成器适用于生成很多的数据,由于它能够在生成数据时保存函数的状况,然后避免占用很多内存;迭代器适用于处理少量数据,由于它需求创立一个目标保护状况。

因而,在实践开发中,咱们能够依据数据量和处理功率的需求来选择运用生成器或迭代器。

2)生成器创立办法

在 Python 中,能够经过以下两种办法创立生成器:

1、经过生成器函数创立

经过在函数中运用 yield 句子,能够将函数变为生成器函数,每次调用生成器函数时,能够生成一个生成器。

例如:

def generator_example():
    yield 1
    yield 2
    yield 3
gen = generator_example()
print(next(gen))
print(next(gen))
print(next(gen))

2、经过生成器表达式创立

生成器表达式是一种简写的生成器创立办法,它根据列表推导式的语法。

例如:

gen = (x for x in range(3))
print(next(gen))
print(next(gen))
print(next(gen))

以上是生成器的两种创立办法,您能够依据实践需求选择运用。

3)生成器表达式

生成器表达式是一种简写的生成器创立办法,它根据列表推导式的语法。

例如:

gen = (x for x in range(3))
print(next(gen))
print(next(gen))
print(next(gen))

在上面的比如中,咱们运用生成器表达式创立了一个生成器,该生成器生成从 0 到 2 的整数。然后,咱们运用 next() 函数逐一迭代生成器中的值。

4)yield关键字

yield 关键字是 Python 中的一个关键字,用于生成器函数中。它答应一个函数在生成值时暂停其履行,以便在稍后康复其履行并生成下一个值。这使生成器函数成为一种特别的函数,能够按需生成一系列值。

5)生成器函数

生成器函数是 Python 中特别的函数,该函数可生成一个生成器。与普通函数不同,生成器函数能够在每次被调用时生成一个生成器,并在生成器中生成一系列值。

生成器函数经过运用 yield 句子创立生成器。每逢函数履行到 yield 句子时,生成器函数的履行就会暂停,并回来 yield 句子后面的值。当再次调用生成器函数时,它将从上次暂停的位置持续履行,直到遇到下一个 yield 句子,或许函数回来。

例如:

def generator_example():
    yield 1
    yield 2
    yield 3
gen = generator_example()
print(next(gen))
print(next(gen))
print(next(gen))

在上面的比如中,咱们界说了一个生成器函数 generator_example,该函数经过运用 yield 句子生成了三个整数:1、2 和 3。然后,咱们经过调用该函数并将其成果分配给生成器 gen 来创立生成器,并运用 next() 函数逐一迭代生成器中的值。

6)return 和 yield 异同

returnyield 都是用于在函数中停止履行的关键字,可是它们的作用是不同的。

  • return:当函数调用 return 时,函数当即停止履行,并回来一个值(如果存在)给调用者。该值一般表明函数的终究成果。

  • yield:当生成器函数调用 yield 时,它仅暂停其履行并生成一个值,但不停止函数。在下一次调用该生成器时,它将康复其履行,直到遇到下一个 yield 或停止函数。

因而,yield 是生成器函数的一个关键字,能够使生成器生成一系列值,而 return 是一般函数的一个关键字,它回来一个值并停止函数

7)yield的运用办法

yield 关键字用于生成器函数。在生成器函数中,咱们能够运用 yield 关键字生成一系列值,而无需暂停整个函数。

例如,以下是运用 yield 关键字的简略生成器函数的比如:

def simple_generator():
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5
gen = simple_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
print(next(gen)) # Output: 4
print(next(gen)) # Output: 5

咱们能够运用 for 循环或 next() 函数迭代生成器中的值,如下所示:

for value in simple_generator():
    print(value)
# Output:
# 1
# 2
# 3
# 4
# 5

【留意】生成器函数只能迭代一次,所以请保证在运用生成器函数时存储其回来值。

8)for与next

在运用生成器时,咱们能够运用两种不同的办法来迭代生成器中的值:for 循环next() 函数。

  • for 循环:经过运用 for 循环,咱们能够在生成器中迭代所有值。例如:
def simple_generator():
    yield 1
    yield 2
    yield 3
for value in simple_generator():
    print(value)
# Output:
# 1
# 2
# 3
  • next() 函数:经过运用 next() 函数,咱们能够手动操控生成器的迭代。例如:
def simple_generator():
    yield 1
    yield 2
    yield 3
gen = simple_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3

【留意】在生成器迭代完所有的值后,再运用 next() 函数将导致抛出 StopIteration 反常。因而,在运用 next() 函数时,请保证捕获该反常。

9)send的运用

send() 办法是生成器的一种办法,它答应咱们在生成器函数内部向生成器发送数据。该办法答应生成器接纳外部数据,并运用这些数据生成成果

在运用 send() 办法时,咱们需求在生成器函数中运用 yield 表达式来接纳数据,如下所示:

def simple_generator():
    result = yield
    print("Received data:", result)
gen = simple_generator()
next(gen)
gen.send("Hello, World!")
# Output: Received data: Hello, World!

【留意】第一次运用 send() 办法之前,咱们需求调用 next() 函数,以启动生成器函数。此外,运用 send() 办法将导致抛出 StopIteration 反常,因而请保证在运用该办法时进行反常处理。

三、协程进阶

协程(Coroutine)是一种编程技巧,用于在多使命环境中完成轻量级的使命切换。与线程不同,协程不需求创立新的体系级线程,并且消耗的资源也比线程更少。因而,协程能够比线程更高效地完成使命。在我上篇的文章已经解说了:IO模型和协程介绍

1)生成器与协程联系

生成器和协程是相关但有所不同的概念。生成器是一种特别的迭代器,能够生成一系列的值,每次迭代时只回来一个值。生成器能够运用 yield 关键字来暂停履行,并在下一次调用时持续履行。

  • 协程是一种并发编程技能,能够在单一线程中完成多个使命的并行履行。与线程不同,协程不需求创立新的体系级线程,并且消耗的资源也比线程更少。协程能够运用 yield 关键字来暂停履行,并在下一次调用时持续履行。

  • 因而,生成器能够用于完成协程,但它们不是协程的必需条件。在 Python 中,能够运用 asyncio 库来完成协程,该库不需求运用生成器。不过,在完成协程时,生成器确实能够作为一种有效的工具,帮助开发者完成协程的暂停和康复。

2)协程完成原理

协程的完成主要是经过一种叫做 “协程调度器” 的技能完成的,这种技能能够在不创立新的线程的情况下,在单一线程中按需切换协程的履行。协程调度器会管理当时正在运转的协程以及等待履行的协程,并在每个协程履行完后切换到下一个协程。

3)协程完成办法

在 Python 中,能够运用 asyncio 库来完成协程。协程函数能够运用 async 关键字符号,表明该函数是一个协程函数。在协程函数中,能够运用 await 关键字等待其他协程完成,然后完成协程的切换。

在 Python 中,能够运用 asyncio 库来完成协程。下面是一个简略的比如:

import asyncio
async def task1():
    print("Task 1 is running")
    await asyncio.sleep(1)
    print("Task 1 is complete")
async def task2():
    print("Task 2 is running")
    await asyncio.sleep(1)
    print("Task 2 is complete")
async def main():
    task1_coro = task1()
    task2_coro = task2()
    await asyncio.gather(task1_coro, task2_coro)
if __name__ == "__main__":
    asyncio.run(main())

上面的代码中,task1task2 是两个协程函数,它们能够在独自的使命中并行履行。main 函数是协程的进口,在这个函数中,咱们创立了两个协程的目标 task1_corotask2_coro,并经过 asyncio.gather 函数将它们并行履行。最终,咱们经过 asyncio.run 函数启动了整个协程。

运转上面的代码,能够得到以下输出:

Task 1 is running
Task 2 is running
Task 1 is complete
Task 2 is complete

能够看到,协程中的使命是并行履行的,因而咱们能够充分利用 CPU 的资源,进步程序的履行功率。


Python 高档编程之生成器与协程进阶解说就先到这里了,有任何疑问欢迎给我留言,后续会持续更新相关技能文章,请小伙伴耐性等待,也能够重视我的公众号【大数据与云原生技能共享】进行深化技能交流~