Python中的一系列承诺

本教程将教我们如何在Python中编写一系列的承诺。首先,我们将讨论Python中的异步编程。

接下来,我们将讨论Python中的回调函数。最后,在进入实际话题之前,我们将简要讨论Python中的try/except ,然后再来讨论Python中的一系列承诺。

Python中的异步编程

这篇文章希望你对操作系统线程有一个基本的概念。如果你没有关于线程的初步介绍,你可以阅读操作系统中的线程作为先决条件。

异步编程允许多个线程并行运行,而主程序(通常称为主/管理线程)可以创建多个工作线程。通常情况下,主线程会等待工作线程,工作线程在完成任务后通知主线程。

与常规编程不同,异步函数不是保持控制权直到竞争,而是暂停并允许其他函数(线程)并行运行。

我们将讨论并给出一个Python中异步编程的例子;然而,最好先看看相关的同步代码。这段代码将有助于通过比较来发展理解。

def count():
    for i in range(5):
        print(i, end=' ')
def main():
    count()
    count()
    count()
main()

在这里,我们依次调用count 函数三次。输出结果与预期一致。

0 1 2 3 4 0 1 2 3 4 0 1 2 3 4

你可以看到第一个count 函数的输出,接着是第二个调用count 函数的输出,然后是最后一个count 函数的输出。

Python的asyncio 库允许在Python中运行异步程序。异步编程的第一个要求是把函数设计成可等待的对象。

将一个标准函数转换为可等待对象有两个要求。第一个是使用async 关键字(在def 关键字之前)来创建异步函数而不是常规函数。

第二个要求是在异步函数内部调用sleep ,暂停当前函数,并控制其他函数。

sleep 语句是代码中具体的点,在这里函数正好进入暂停状态。异步编程的第二个要求是在调用可等待对象(异步函数)时添加await ,否则将出现错误。

await 关键字告诉事件循环暂停当前函数以给其他函数提供运行时间。

第三个要求是调用gather 函数并传递可等待对象(异步函数)。gather 函数按照它们的顺序运行这些函数,但是是并发的。

这意味着第一个函数首先启动,一段时间后,第二个函数也平行启动。类似地,所有的异步函数都是一个接一个地同时开始运行。

现在,让我们看看这段代码。

import asyncio
async def count():
    for i in range(5):
        print(i, end=' ')
        await asyncio.sleep(0.5)
async def main():
    await asyncio.gather(count(), count(), count())
if __name__ == "__main__":
	asyncio.run(main())

在这里,我们将之前的代码转换为异步代码,并添加了一些内容。在第一行,asyncio 库被导入。

在所有函数的开头都添加了async 关键字。

sleep 函数调用被添加到count 函数中。await 关键字被添加到所有的函数调用中,包括main 函数。

最后,gather 函数被调用到main ,其中count 函数被多次调用,以证明每个函数调用是一个独立的线程。

使用gather 函数,我们添加可等待的对象,形成一组异步函数,以并发运行。让我们看看这段代码的输出。

0 0 0 1 1 1 2 2 2 3 3 3 4 4 4

在输出中,你可以看到所有的线程都是并行运行的,并产生异步输出,而不是完全运行。

你可能会与多次调用同一函数相混淆;这里是另一个例子,我们有不同的函数在并行运行。

import asyncio
async def count1():
    for i in range(10):
        print(i, end=' ')
        await asyncio.sleep(0.5)
async def count2():
    for i in range(50,60):
        print(i, end=' ')
        await asyncio.sleep(0.5)
async def main():
    await asyncio.gather(count1(), count2())
asyncio.run(main())

这段代码的输出是:

0 50 1 51 2 52 3 53 4 54 5 55 6 56 7 57 8 58 9 59

同样,这两个函数都在并发地运行。

回调函数

一个回调被传递给另一个函数(作为一个参数)。另一个函数应该在其定义的某个地方回调这个函数。

然而,调用点取决于另一个函数是如何定义的。

在这里,我们有一个与回调函数有关的简单的编码例子。

import random as r
def callback1(s):
    print(f'******* {s} *******')
def callback2(s):
    print(f'^^^^^^^ {s} ^^^^^^^')
def print_through_callback(message, f1, f2):
    if r.randint(0,1) == 0:
        f1(message)
    else:
        f2(message)
def main():
	print_through_callback("Callback Example", callback1, callback2)
main()

在这段代码中,我们的print 函数有三个参数。第二个和第三个参数是一些函数名称。

main ,我们传递两个函数,代码随机调用一个。如果你多次运行这段代码,你可以看到两个函数都是随机调用的。

try/except 在Python中

Python也提供了异常处理。在Python中,我们有一个try 块来测试代码;它有可能出现异常,在except 块中,你可以处理这个异常。

我们都知道除以0是没有定义的,程序(几乎每一种编程语言)都会崩溃;当我们调用除以0的操作时。如果你不知道,那就试试这段代码。

def main():
        x = int(input('Enter any number:'))
        print (2/x)
main()

输入零,看看结果;你的程序会崩溃。代码的崩溃是一件坏事,应该通过异常处理来避免。

请看有异常处理的相同代码。

def main():
    try:
        x = int(input('Enter any number:'))
        print (2/x)
    except:
        print ('Divide by zero is not defined')
main()

你应该运行这段代码并输入非零值;你将得到除法运算的结果,写在print (2/x) ;如果你输入零,程序将给出消息Divide by zero is not defined ,而不是崩溃。

Python中的系列承诺

回调函数与普通函数相同;但是,它们的用途不同。

考虑一下需要大量时间执行的重量级函数。通常情况下,这样的函数被做成异步的。

异步函数将在后台执行,在一个特定的时间后,它们将完成,而其他函数将以并行方式启动。然而,如果你想在完成一些重度函数后运行一些函数,选择是使用回调函数。

然而,这种任务的完成有一个问题。如果任务在完成之前抛出一个异常怎么办?

为了确保在成功完成任务后调用该函数,需要使用承诺和异步编程。

许诺

诺言是一个对象,表示异步函数的成功或不成功(失败)完成。

许诺对象也代表异步函数的结果值。一个承诺被用来管理与多个回调有关的问题。

你可以使用promise API来执行Python中的一系列承诺。然而,我们可以通过async/await ,在Python中实现同样的目的。

为了在Python中用异步函数实现,我们必须使用带有异步函数的asyncio 库。我们可以通过await 关键字依次调用函数,这在上面已经介绍过了。

最后,我们要使用try/except 块。首先,请看代码和输出。

稍后,我们将解释try/except 块的目的。

import asyncio
import random as r
async def f1(x):
    await asyncio.sleep(1)
    return x ** 2
async def f2(x):
    await asyncio.sleep(1)
    return x / 2
async def f3(x):
    if r.randint(0, 1) == 0:
        return x
    raise ValueError(x)
async def run():
    try:
        value = await f3(await f2(await f1(r.randint(5,9))))
    except ValueError as exception:
        print('Exception Occurred:', exception.args[0])
    else:
        print('No Exception:', value)
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())
    loop.close()

下面是上述Python脚本运行5次后的综合输出。

No Exception: 12.5
Exception Occurred: 12.5
Exception Occurred: 32.0
No Exception: 18.0
No Exception: 40.5

这个输出只是描述我们在一些异步操作中可能会有成功或失败。因此,我们可以在try 块中的函数调用之后放置一个回调函数的语句。

在成功完成的情况下,代码将执行回调函数。在失败的情况下,控制将转到except 块并忽略回调函数。

通过这种方式,我们可以在Python中处理一系列的承诺;如果我们有成功的完成,那么就执行回调函数(这取决于一些所需任务的成功完成);否则,我们不必执行我们的回调函数。