文章目录
- 一、概述
- 二、生成器
- 1)生成器和迭代器的区别
- 2)生成器创建方式
- 1、通过生成器函数创建
- 2、通过生成器表达式创建
- 3)生成器表达式
- 4)yield关键字
- 5)生成器函数
- 6)return 和 yield 异同
- 7)yield的使用方法
- 8)for与next
- 9)send的使用
- 三、协程进阶
- 1)生成器与协程关系
- 2)协程实现原理
- 3)协程实现方式
一、概述
-
生成器是一种在 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 高级编程之生成器与协程进阶讲解就先到这里了,有任何疑问欢迎给我留言,后续会持续更新相关技术文章,请小伙伴耐心等待,也可以关注我的公众号【大数据与云原生技术分享】进行深入技术交流~