一、概述
-
生成器是一种在 Python 中的迭代器生成器。生成器是一个函数,它生成一个迭代器。当生成器函数被调用时,它不会当即履行,而是回来一个生成器目标,该目标能够被用于迭代。生成器能够利用 yield 句子在函数内部生成值,并在函数调用者处接纳这些值。
-
协程是一种高效的、内存友爱的、线程内的并发技能,它能够让您在单个线程内并发地履行多个使命。协程是经过运用 async 关键字完成的,并能够在 Python 中的
asyncio
库中运用。与线程不同,协程不需求额外的体系线程,因而它们比线程更高效、更灵敏。
在简略的说法,生成器用于生成一系列的值,而协程用于在单个线程中并发履行多个使命。
二、生成器
生成器表达式本质上便是一个迭代器,是界说迭代器的一种办法,是答应自界说逻辑的迭代器。生成器运用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 中的两个相关的概念,可是有一些差异:
-
界说:生成器是一种特别的迭代器,它能够生成一系列的值,而迭代器是一个目标,它完成了 iter 和 next 办法,能够回来一个值的序列。
-
创立:生成器能够经过界说生成器函数,在函数内部运用 yield 句子生成值;迭代器能够经过界说迭代器类,在类中完成 iter 和 next 办法。
-
功率:生成器函数在生成值时只需求暂停函数的履行,因而它具有更高的功率;迭代器类需求保护一个目标状况,因而功率较低。
-
用处:生成器适用于生成很多的数据,由于它能够在生成数据时保存函数的状况,然后避免占用很多内存;迭代器适用于处理少量数据,由于它需求创立一个目标保护状况。
因而,在实践开发中,咱们能够依据数据量和处理功率的需求来选择运用生成器或迭代器。
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 异同
return
和 yield
都是用于在函数中停止履行的关键字,可是它们的作用是不同的。
-
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())
上面的代码中,task1
和 task2
是两个协程函数,它们能够在独自的使命中并行履行。main
函数是协程的进口,在这个函数中,咱们创立了两个协程的目标 task1_coro
和 task2_coro
,并经过 asyncio.gather
函数将它们并行履行。最终,咱们经过 asyncio.run
函数启动了整个协程。
运转上面的代码,能够得到以下输出:
Task 1 is running
Task 2 is running
Task 1 is complete
Task 2 is complete
能够看到,协程中的使命是并行履行的,因而咱们能够充分利用 CPU 的资源,进步程序的履行功率。
Python 高档编程之生成器与协程进阶解说就先到这里了,有任何疑问欢迎给我留言,后续会持续更新相关技能文章,请小伙伴耐性等待,也能够重视我的公众号【大数据与云原生技能共享】进行深化技能交流~