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中处理一系列的承诺;如果我们有成功的完成,那么就执行回调函数(这取决于一些所需任务的成功完成);否则,我们不必执行我们的回调函数。