PEP 3152 – 协函数
- 作者:
- Gregory Ewing <greg.ewing at canterbury.ac.nz>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2009年2月13日
- Python 版本:
- 3.3
- 发布历史:
摘要
提议了一种用于定义和调用一种特殊类型的生成器“协函数”的语法。它旨在提供一种简化的编写基于生成器的协程的方法,并允许及早检测某些容易出错的代码类型,否则这些错误往往会导致难以诊断的症状。
本提案基于PEP 380中描述的“yield from”机制,并在此基础上阐述了协函数的某些语义。但是,如果愿意,也可以独立于PEP 380来定义和实现协函数。
拒绝
请参阅https://mail.python.org/pipermail/python-dev/2015-April/139503.html
规范
协函数定义
引入了一个新的关键字codef,它用于代替def来定义一个协函数。协函数是一种特殊的生成器,具有以下特征:
- 协函数始终是生成器,即使它不包含任何
yield或yield from表达式。 - 协函数不能像普通函数那样调用。如果尝试普通调用协函数,将引发异常。
协调用
一个协函数对另一个协函数的调用是通过在新关键字cocall上来标记调用的。表达式
cocall f(*args, **kwds)
语义上等同于
yield from f.__cocall__(*args, **kwds)
但__cocall__返回的对象预计是一个迭代器,因此跳过了对其调用iter()的步骤。
协调用表达式的完整语法由以下语法行描述:
atom: cocall | <existing alternatives for atom>
cocall: 'cocall' atom cotrailer* '(' [arglist] ')'
cotrailer: '[' subscriptlist ']' | '.' NAME
cocall关键字仅在协函数内部语法上有效。如果在任何其他上下文中使用了它,将导致SyntaxError。
实现__cocall__的对象预计会返回一个遵守迭代器协议的对象。协函数对__cocall__的响应与普通生成器函数对__call__的响应相同,即返回一个生成器迭代器。
某些包装其他可调用对象的对象,特别是绑定方法,将具有委托给底层对象的__cocall__实现。
新的内置函数、属性和C API函数
为了便于协函数与非协程代码进行接口,将有一个内置函数costart,其定义等同于:
def costart(obj, *args, **kwds):
return obj.__cocall__(*args, **kwds)
还将有一个相应的C API函数:
PyObject *PyObject_CoCall(PyObject *obj, PyObject *args, PyObject *kwds)
目前尚不确定协函数是否是一种独立的类型,还是像生成器函数一样,仅仅是一个特殊标记的函数实例。如果是后者,则应提供一个只读的布尔属性__iscofunction__,以便能够测试给定的函数对象是否为协函数。
动机与原理
当yield from语法用于将生成器的一部分工作委托给另一个函数时,该语法是相当不言自明的。它也可以在基于生成器的协程的实现中发挥作用,但用于此目的时读起来有些笨拙,并且容易模糊代码的真实意图。
此外,将生成器用作协程有时容易出错。如果忘记在应该使用yield from时使用它,或者在不应该使用时使用它,所产生的症状可能会晦涩难懂和令人困惑。
最后,有时需要一个函数成为协程,即使它不产生任何东西,在这种情况下,有必要求助于一些技巧,如if 0: yield来强制它成为一个生成器。
codef和cocall构造通过使语法直接反映意图来解决第一个问题,即该函数构成协程的一部分。
第二个问题通过不允许以无意义的方式混合协程和非协程代码来解决。如果违反了规则,将引发一个异常,精确地指出问题所在和发生位置。
最后,通过使定义的形式决定函数是否为协程,而不是它包含的内容,消除了对虚拟yield的需求。
原型实现
可以在此处找到Python 3.1.2的补丁形式的实现:
http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cofunctions.html
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-3152.rst