PEP 288 – 生成器属性和异常
- 作者:
- Raymond Hettinger <python at rcn.com>
- 状态:
- 已撤回
- 类型:
- 标准跟踪
- 创建日期:
- 2002年3月21日
- Python 版本:
- 2.5
- 发布历史:
摘要
本 PEP 提议通过提供用于引发异常和与运行中的生成器共享数据的机制来增强生成器。
状态
本 PEP 已撤回。异常引发机制已扩展并包含在 PEP 343 中。属性传递功能从未获得支持,没有明确的实现方式,也没有简洁的方式让正在运行的生成器访问自己的命名空间。
基本原理
目前,只有基于类的迭代器可以提供属性和异常处理。然而,基于类的迭代器更难编写,更不紧凑,可读性更差,速度也更慢。更好的解决方案是为生成器启用这些功能。
启用属性赋值允许数据在运行中的生成器之间传递。使用属性共享数据的方法在 Python 中非常普遍。虽然存在其他方法,但相比之下,这些方法有些笨拙。
另一个演进步骤是添加一个生成器方法,允许将异常传递给生成器。目前,没有简洁的方法可以从生成器外部触发异常。此外,生成器异常传递有助于缓解生成器对 try/finally 的禁止。对于需要刷新缓冲区或在终止时关闭资源的生成器来说,这种需求尤为迫切。
这两个提案都是向后兼容的,并且不需要新的关键字。它们被推荐用于 Python 2.5 版本。
生成器属性规范
本质上,该提案是模仿类的属性写入。唯一的问题是生成器缺乏引用自身实例的方式。因此,该提案是提供一个用于发现引用的函数。例如
def mygen(filename):
self = sys.get_generator()
myfile = open(filename)
for line in myfile:
if len(line) < 10:
continue
self.pos = myfile.tell()
yield line.upper()
g = mygen('sample.txt')
line1 = g.next()
print 'Position', g.pos
生成器属性的用途包括
- 向生成器客户端提供额外信息(如上所示)。
- 外部设置控制标志以管理生成器操作(可能告知生成器何时进入或跳过数据组)。
- 编写具有复杂执行状态的惰性消费者(例如,算术编码器输出流)。
- 编写协程(如 Mertz 博士的文章 [1] 中所示)。
“yield” 和 “next” 的控制流不受此提案的更改。“yield” 和 “next” 的控制流保持不变。唯一的改变是可以将数据传入和传出生成器。大部分底层机制已经到位,只需要添加访问函数。
生成器异常传递规范
向生成器接口添加一个 .throw(exception) 方法
def logger():
start = time.time()
log = []
try:
while True:
log.append(time.time() - start)
yield log[-1]
except WriteLog:
writelog(log)
g = logger()
for i in [10,20,40,80,160]:
testsuite(i)
g.next()
g.throw(WriteLog)
目前没有解决在生成器内部触发异常的办法。这是 Python 中唯一一个主动代码无法被捕获或通过的场景。
生成器异常传递还有助于解决生成器的一个内在限制,即禁止使用 try/finally 来触发清理代码(PEP 255)。
注意 A:选择 throw 方法名有几个原因。Raise 是一个关键字,因此不能用作方法名。与立即从当前执行点引发异常的 raise 不同,throw 会先返回到生成器,然后引发异常。throw 这个词暗示将异常放在另一个位置。throw 这个词在其他语言中已经与异常相关联。
考虑过其他方法名:resolve()、signal()、genraise()、raiseinto() 和 flush()。没有一个比 throw() 更合适。
注意 B:为了保持 throw() 语法的简单性,只支持 raise 语法的实例版本(不支持“raise string”或“raise class, instance”的变体)。
调用 g.throw(instance) 将相当于在最近一次 yield 之后立即写入 raise instance。
参考资料
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0288.rst