python中的未来函数 python未来方向( 六 )


在我为这本书做研究的过程中 , 我确信API方法asyncio.ensure_future()是引起对asyncio库广泛误解的罪魁祸首 。API的大部分内容都非常清晰,但在学习过程中还存在一些严重的障碍,这就是其中之一 。当你遇到ensure_future()时 , 你的大脑会非常努力地将其集成到关于asyncio应该如何使用的心理模型中——但很可能会失败!
在Python 3.6 asyncio 文档中,这个现在已经臭名昭著的解释突出了 ensure_future() 的问题:
asyncio.ensure_future(coro_or_future, *,_loop =None)
安排执行一个协程对象:把它包装在future中 。返回一个Task对象 。如果参数是Future,则直接返回 。
什么!? 当我第一次读到这篇文章时,我很困惑 。下面希望是对ensure_future()的更清楚的描述:
这个函数很好地说明了针对终端用户开发人员的asyncio API(高级API)和针对框架设计人员的asyncio API(低级API)之间的区别 。让我们在示例 3-18中自习看看它是如何工作的 。
示例 3-18. 仔细看看ensure_future()在做什么
(L3)一个简单的什么都不做的协程函数 。我们只需要一些能组成协程的东西 。
(L6)我们通过直接调用该函数来创建协程对象 。你的代码很少会这样做 , 但我想在这里明确地表示 , 我们正在向每个create_task()和ensure_future()传递一个协程对象 。
(L7)获取一个循环 。
(L9)首先,我们使用loop.create_task()在循环中调度协程,并返回一个新的Task实例 。
(L10)验证类型 。到目前为止,没有什么有趣的 。
(L12)我们展示了asyncio.ensure_future()可以被用来执行与create_task()相同的动作:我们传入了一个协程,并返回了一个Task实例(并且协程已经被安排在循环中运行)!如果传入的是协程,那么loop.create_task()和asyncio.ensure_future()之间没有区别 。
(L15)如果我们给ensure_future()传递一个Task实例会发生什么呢?注意我们要传递的Task实例是已经在第4步通过loop.create_task()创建好的 。
(L16)返回的Task实例与传入的Task实例完全相同:它在被传递时没有被改变 。
直接传递Future实例的意义何在?为什么用同一个函数做两件不同的事情?答案是,ensure_future()的目的是让框架作者向最终用户开发者提供可以处理两种参数的API 。不相信我?这是ex-BDFL自己说的:
ensure_future()的要点是,如果你有一个可能是协程或Future(后者包括一个Task,因为它是Future的子类)的东西,并且你想能够调用一个只在Future上定义的方法(可能唯一有用的例子是cancel()) 。当它已经是Future(或Task)时,它什么也不做;当它是协程时,它将它包装在Task中 。
如果您知道您有一个协程,并且希望它被调度,那么正确的API是create_task() 。唯一应该调用ensure_future()的时候是当你提供一个API(像大多数asyncio自己的API),它接受协程或Future,你需要对它做一些事情,需要你有一个Future 。
—Guido van Rossum
总而言之 , asyncio.sure_future()是一个为框架设计者准备的辅助函数 。这一点最容易通过与一种更常见的函数进行类比来解释,所以我们来做这个解释 。如果你有几年的编程经验,你可能已经见过类似于例3-19中的istify()函数的函数 。示例 3-19中listify()的函数 。
示例 3-19. 一个强制输入列表的工具函数
这个函数试图将参数转换为一个列表 , 不管输入的是什么 。api和框架中经常使用这类函数将输入强制转换为已知类型 , 这将简化后续代码——在本例中,您知道参数(来自listify()的输出)将始终是一个列表 。
如果我将listify()函数重命名为ensure_list(),那么您应该开始看到与asyncio.ensure_future()的类似之处:它总是试图将参数强制转换为Future(或子类)类型 。这是一个实用函数,它使框架开发人员(而不是像你我这样的终端用户开发人员)的工作变得更容易 。

推荐阅读