Python 事件驱动编程

2024年8月29日 | 阅读 10 分钟

在本教程中,我们将学习事件驱动编程以及可用于进行 Python 事件驱动编程的 Python 模块 (Asyncio)。

事件驱动编程

最终,程序的流程取决于事件,而专注于事件的编程称为事件驱动编程。我们之前只处理并行或顺序模型,但现在我们将讨论异步模型。遵循事件驱动编程概念的编程模型称为异步模型。事件驱动编程的工作方式取决于程序中发生的事件。

除此之外,它还取决于程序的事件循环,事件循环始终监听程序中新的传入事件。一旦事件循环在程序中启动,那么只有事件才能决定什么将执行以及以什么顺序执行。

请看下面的事件循环流程图,以了解事件在事件驱动编程中的工作方式

Asyncio - Python 事件驱动编程模块

asyncio 模块在 Python 3.4 版本中加入,并且在所有更高版本的 Python 中都可用。Asyncio 模块为使用程序中的协程以单线程方式编写并发代码提供了非常好的基础设施。

在 Python 的 asyncio 模块中,进行事件驱动编程时使用以下不同的概念:

  • 事件循环
  • 期货
  • 协程
  • @asyncio.coroutine 装饰器
  • 任务
  • 传输
  • 和协议

让我们详细了解 asyncio 模块使用的所有这些不同概念,并了解它们在进行事件驱动编程时的工作方式。

事件循环

事件循环是 asyncio 模块的一个功能,用于处理计算程序中发生的所有事件。事件循环在整个程序执行期间充当循环器,它还跟踪已执行和新传入的事件。asyncio 模块的主要优点之一是它每个进程只允许一个事件循环。

在 asyncio 模块中,我们有一些方法可以管理代码中的事件循环。asyncio 模块中提供了以下此类方法。

  • time() - 我们可以使用此方法根据事件循环中存在的内部时钟向用户返回当前时间。
  • loop = get_event_loop() - 通过使用此方法,我们将根据程序中当前执行的上下文返回事件循环。
  • call_soon(CallbackFunction, ArgumentGiven) - 使用此函数,我们可以安排尽快调用回调函数。当控制返回到事件循环并且 call_soon() 返回后,将调用给定参数中的回调函数。
  • call_later(time_to_delayed, CallbackFunction, ArgumentGiven) - 使用此方法,我们可以安排在提供的时间延迟(以秒为单位)后调用方法中给定的回调函数。
  • new_event_loop() - 使用此方法,我们可以创建并返回一个新的事件循环项目。
  • set_event_loop() - 借助此方法,我们可以将程序中当前执行的上下文的事件循环设置为该循环。
  • run_forever() - 通过在程序中使用 loop.run_forever() 方法,我们可以运行一个循环,直到调用 stop() 方法。

现在,我们将在下面的示例中查看一个 Python 程序,其中我们将使用事件循环方法,即 get_event_loop() 方法。通过使用此方法,我们将打印我们在事件中给出的命令。

示例:请看以下带有事件循环的 Python 程序

输出

Printing this text through the event loop

说明

我们首先在程序中导入了 asyncio 模块以使用事件循环方法。

之后,我们定义了一个默认函数,其中我们将“loop”作为参数并在函数内部打印命令。我们使用了事件循环中的 stop() 方法来停止事件。

之后,我们在 loop 参数上使用了 get_event_loop() 方法来打印默认函数中的文本。然后,在 call_soon() 事件循环方法中,我们使用了函数名和函数参数作为参数。最后,我们使用了 run_forever() 和 close() 事件循环方法。

期货

asyncio 模块中给出的 Future 类与 concurrent.futures 方法兼容。asyncio 模块中给出的 Future 类表示程序中尚未完成的计算。

concurrent.futures.Future 和 asyncio.futures 之间存在一些主要区别。

  • 我们使用 add_done_callback() 方法在 Future 类中注册的回调函数将始终仅通过事件循环中的 call_soon() 方法调用。
  • Future 类中的 exception() 和 result() 方法不会接受 timeout 或 time 给定参数,并且当 Future 在这些函数上尚未完成时,它们将在输出中显示错误。
  • 我们不能将 asyncio.futures.Future 类与 concurrent.futures 包中存在的 as_completed() 或 wait() 函数一起使用,因为它与它们不兼容。

现在,我们将在下面的示例中查看一个 Python 程序,其中我们将使用 asyncio 模块中的 Future 类方法并在输出中打印文本。

示例

输出

This text is printed using future class methods!

说明

我们首先定义了一个默认函数,即“myFunction”,并以 Future 作为参数。在函数内部,我们使用了 sleep() 方法作为执行的两次暂停。

然后,我们使用 Future 类方法给出我们想要在结果中打印的文本。我们在程序中使用了事件循环中的 get_event_loop 方法。然后,我们在默认函数中给定的 Future 参数上使用了 future() 类方法。

现在,我们可以在输出中打印文本,因为我们已经使用了 Future。为了在输出中打印文本,我们使用了 try 和 finish 方法,其中在 try 方法中,我们调用了打印命令,在 finish 方法中,我们使用 close() 方法关闭了循环。

协程

asyncio 模块中协程的概念与 threading 模块中线程对象的协程概念非常相似。

asyncio 模块中协程的这个概念是子例程概念的泛化形式。

我们甚至可以在程序执行期间暂停协程,以便暂停的协程将等待用户给定的外部处理。暂停的协程只有在外部处理完全完成后才会返回到上次暂停的位置。

在 asyncio 模块协程中,我们可以使用以下两种方式来帮助我们在程序中实现协程

  1. @asyncio.coroutine 装饰器
  2. async def function()

让我们通过在 Python 程序中实现它们来理解这两种方式。

1. @asyncio.coroutine 装饰器

我们可以通过利用生成器和 asyncio 模块装饰器(即 @asyncio.coroutine 装饰器)在程序内部实现协程。我们可以通过以下示例了解使用装饰器实现协程的方法。

示例:请看以下 Python 程序

输出

This text is present inside the coroutine of the asyncio module!

说明

我们在导入 asyncio 模块后使用了 @asyncio.coroutine 装饰器。然后,我们使用了一个默认函数来使用协程方法获取文本。之后,我们使用了事件循环中的 get_event_loop() 方法来在输出中打印文本。最后,我们对默认函数使用了“try and finally”方法,并使用 close() 函数关闭了程序中的循环。

2. async def function()

我们可以说 async def function() 是 asyncio 模块实现协程最通用的方法。我们可以通过以下示例了解使用 def function() 实现协程的方法。

示例:请看以下 Python 程序

输出

This text is present inside the coroutine of the asyncio module!

说明

与实现协程的第一种方式一样,我们在此方法中也遵循了相同的路径。在此方法中,我们没有使用装饰器然后定义协程的默认函数,而是直接使用了 asyncio 模块的 async def operationCoroutine() 函数来实现协程。

任务

任务是 asyncio 模块中给出的一个子类,负责以并行执行方式在事件循环中执行 asyncio 协程。我们可以通过使用 Python 程序执行协程来了解任务子类的工作方式。

示例

输出

Loop event is processing coroutine no: 0
Loop event is processing coroutine no: 1
Loop event is processing coroutine no: 2
Loop event is processing coroutine no: 3
Loop event is processing coroutine no: 4
Loop event is processing coroutine no: 5
Loop event is processing coroutine no: 6
Loop event is processing coroutine no: 7
Loop event is processing coroutine no: 8
Loop event is processing coroutine no: 9
All given tasks are completed

说明

我们已经在程序中导入了 asyncio 和 time 模块以使用其函数。然后,我们使用了一个异步默认函数来设置打印已处理协程的任务。

我们使用了 time 模块的 sleep() 函数,以便在打印每个已执行协程后暂停 2 秒。

然后,我们使用了另一个异步默认函数来设置其中的任务循环。完成循环后,该函数将打印“任务完成”。最后,我们在程序中使用了事件循环方法来运行和关闭循环。

传输

传输是 asyncio 模块中提供给我们的类,我们可以使用它们在程序中实现各种类型的通信。传输类不是线程安全的,并且在建立通信通道后,我们始终必须将它们与协议实例配对。

在 asyncio 传输类中,以下类型的传输可以从基本传输类中继承到程序中:

  1. 数据报传输:数据报传输是用于发送数据的接口。
  2. 读取传输:读取传输是仅具有只读模式的传输类的接口。
  3. 写入传输:此传输是仅具有只写模式的继承传输类的接口。
  4. 基本子进程传输:基本子进程传输类的工作方式与基本传输类非常相似。

在所有上述继承的传输类中,只有以下不同类型的方法随后从基本传输类中瞬时提供:

  1. is_closing(): 仅当参数中给定的传输类已关闭或正在关闭时,此方法才返回 true。
  2. close(): 此方法用于关闭程序中当前运行的传输类。
  3. get_protocol(): 我们可以在传输类中使用 get_protocol() 方法来返回当前协议。
  4. get_extra_info(className, default = none):我们可以使用此方法获取有关我们在参数中给出的传输类的一些附加信息。

协议

在 asyncio 模块中,我们提供了几个基类,我们可以使用它们在子类中实现我们的网络协议。我们可以将此类与传输类结合使用。协议将请求传出数据并解析传入数据,而传输类负责缓冲和实际 I/O。

以下是协议的三种类型:

  1. Protocol 类:它是协议中的基类,我们可以使用它来实现流协议以与 SSL 和 TCP 传输一起使用。
  2. Datagram protocol 类:它是协议中的另一个基类,我们可以使用它来实现数据报协议以与 UDP 传输一起使用。
  3. Subprocess protocol 类:我们可以使用协议中的此基类来实现各种协议,以使用一组单向管道与子进程通信。

下一主题Python 信号量