这是一份从Python3.3开始的Python3异步I/O提议。研究从PEP 3153缺失的具体提议。 这提议包括了一个可插入式的事件循环API,传输和与Twisted相似的协议抽象,以及来自(PEP 380) 基于yield的更高级的调度器。一份作品里的参考实现,它的代码命名为Tulip(Tulip的repo的链接放在文章最后的参考文献分段里)。
事件循环常用于互操作性较高的地方。对于像Twisted、Tornado或者ZeroMQ这类(基于Python3.3的)框架,它应该是容易去根据框架的需求通过轻量级封装或者代理去适配默认的事件循环实现,或者是用它们自己的事件循环实现去替代默认的实现。(一些像Twisted的框架,拥有多种的事件循环实现。由于这些实现都具有统一的接口,所以这应该不会成为问题。)
事件循环甚至有可能存在两个不同的第三方框架交互,通过共享默认事件循环实现(各自使用自己的适配器),或者是通过共享其中一个框架的事件循环实现。在后者,两种不同级别的适配可能会存在(从框架A的事件循环到标准事件循环接口,然后从标准的再到框架B的事件循环)。被使用的事件循环实现应该在主程序的控制之下(尽管提供了事件循环可以选择的默认策略)。因此,两个单独的API被定义:
一个事件循环实现可能提供额外的方法和保证。
事件循环接口不取决于产量,相反,它使用一个回调,额外接口(传输协议和协议)以及期货(?)的组合。后者类似于在PEP3148中定义的接口,但有不同的实现而且不绑定到线程。特别是,它们没有wait()方法,用户将使用回调。
对于那些不喜欢用回调函数的人(包括我),(Python)提供了一个写异步I/O代码的调度器作为协同程序,它使用了PEP 380的yield from表达式。这个调度器并不是可插入的;可插入性存在于事件循环级别,同时调度器也应该工作在任何符合标准的事件循环实现上。
对于那些在使用协同程序和其他异步框架的代码的互操作性,调度器有一个行为上类似Future的Task类。一个在事件循环级别进行交互操作的框架能够通过加入一个回调函数到Future中,同时等待一个Future来完成。同样的,调度器提供一个操作来挂起协同程序,直到回调函数被调用。
通过事件循环接口来为线程间交互操作提供限制;(Python里)有一个API能够提交一个函数到一个能够返回兼容事件循环的Future的执行器(看 PEP 3148)。
像Stackless Python或 greenlets/gevent 的系统互操作性不是本教程的目的。
Python3.3是必需的。不需超过Python3.3范围的新语言或标准库。不需要第三方模块或包。
这里的规范会放在一个新的顶层包。不同的组件会放在这个包的不同子模块里。包会从各自的子模块中导入常用的API,同时使他们能作为包的可用属性(类似电子邮件包的做法)。
顶层包的名字目前还没指定。参考实现使用“tulip”来命名,但是这个名字可能会在这个实现加入到标准库的时候改变为其他更为烦人的名字(有希望在Python3.4中)。
在烦人的名字选好之前,这篇教程会使用“tulip”作为顶层包的名字。假定没有给定模块名的类和函数都通过顶层包来访问。
要获取当前的事件循环,可以使用get_event_loop()。这函数返回一个在下面有定义的EventLoop类的实例或者一个等价的对象。get_event_loop()可能根据当前线程返回不同的对象,或者根据其他上下文的概念返回不同对象。
要设置当前事件循环,可以使用set_event_loop(event_loop),这里的event_loop是EventLoop类的实例或者等价的实例对象。这里使用与get_event_loop()相同的上下文的概念。还有第三个策略函数:new_event_loop(),有利于单元测试和其他特别的情况,它会创建和返回一个新的基于该策略的默认规则的EventLoop实例。要使它成为当前的事件循环,你需要调用set_event_loop()。
要改变上述三个函数的工作方式(包括他们的上下文的概念),可以通过调用set_event_loop_policy(policy),其中参数policy是一个事件循环策略对象。这个策略对象可以是任何包含了类似上面描述的函数表现(get_event_loop(),set_event_loop(event_loop)和new_event_loop())的对象。默认的事件循环策略是DefaultEventLoopPolicy类的一个实例。当前事件循环策略对象能够通过调用get_event_loop_policy()来取回。
一个事件循环策略没强制要求只能有一个事件循环存在。默认的事件循环策略也没强制要求这样做,但是它强制要求每个线程只能有一个事件循环。
关于时间:在 Python 中,所有的超时(timeout),间隔(interval)和延时(delay)都是以秒来计算的,可以是整型也可以是浮点型。时钟的精准度依赖于具体的实现;默认使用 time.monotonic()。
关于回调(callbacks)和处理函数(handlers):如果一个函数接受一个回调函数和任意个数的变量作为参数,那么你也可以用一个处理函数对象(Handler)来替换回调函数。这样的话就不需要再传递那些参数。这个处理函数对象应该是一个立即返回的函数(fromcall_soon()),而不是延迟返回的(fromcall_later())。如果处理函数已经取消,那么这个调用将不起作用。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务