Ruby生成器与Python生成器

20
我一直在研究Ruby和Python生成器(在Ruby中称为Enumerators)之间的相似性/差异,目前看来它们几乎是等效的。
然而,我注意到一个区别是Python生成器支持close()方法,而Ruby生成器则不支持。从Python文档中可以看到close()方法做如下操作:
引发GeneratorExit异常以终止生成器函数所在的位置。如果生成器函数随后引发StopIteration异常(通过正常退出或由于已经关闭)或者GeneratorExit异常(未捕获异常),则close()返回其调用者。
Ruby Enumerators没有支持close()方法,这是有充分理由还是意外遗漏呢?
此外,我还发现Ruby Enumerators支持rewind()方法,而Python生成器不支持......这也有原因吗?
谢谢。

好奇,但我不明白你如何使用它:你能举个例子吗? - Andrew Vit
5
@Andrew Vit:这可以用来清理生成器持有的资源,如数据库连接、文件等。它还将防止代码中其他部分对其nextsend方法的进一步调用。例如,您可以调用 close 来表示从多个消费者中的一个指示已找到期望的值。 - intuited
@intuited,Python的close()实际上常用吗?我记得在某个地方读到过,它被Python社区认为是“神秘的”,并没有真正被使用。 - horseyguy
3
close() 是用于与生成器进行双向通信并将其转换为“协程”的 API 的一部分。你可以在 http://www.python.org/dev/peps/pep-0342/ 上了解更多信息。Ruby 是否具有这个概念? - alexis
3个回答

7
这篇关于回滚方法的文档详细程度有些欠缺。但是,为了“重新开始”,生成器必须执行以下两个操作之一:
  • 记住它的完整输出,重复输出一次后回滚,然后恢复之前的操作。
  • 以一种使相同输出被重复而不产生其他不需要的副作用的方式重置其内部状态。
第二种情况并非总是可行的;例如,如果生成器从网络中发出字节缓冲区,则输出并不完全是内部状态的函数。但是,任何使用第一种技术的生成器必须随着使用而在内存中构建越来越大的缓冲区。这样的生成器与列表相比提供的性能优势很小。
因此,我得出结论:Ruby的rewind方法必须是可选的,并非所有具体枚举器类都支持该方法。因此,如果Python设计者重视Liskov替换原则,那么他们不会要求所有生成器都必须拥有这样的方法。

2

生成器是基于堆栈的,Ruby的枚举器通常是特殊的(在解释器级别上)而不是基于堆栈的。


1

Ruby的Enumerator在内部使用StopIteration类,参见Ruby 1.9.1中的枚举器是如何工作的?

(如果您在for each调用中使用它,它只是被包装起来了)。所以我想说它们非常接近。话虽如此,我不确定枚举器上的close方法应该做什么,可能是清理吧?(Python的生成器可能会受益于一个rewind--请注意,在Ruby中,一些枚举器不响应rewind,因此当您调用该方法时,它们会引发异常)。


2
谢谢你的回答。但是StopIteration也是Python使用的 - 实际上Ruby从Python借鉴了这个想法,呵呵。至于close()可能会做什么,请参考intuited在我的问题(上面)中的评论。 - horseyguy
是的,Ruby 1.9.x 的 Enumerator 基本上将其与 Python 的 Generator 同步了(虽然你也可以在 Ruby 中使用块来模拟生成器)。 - rogerdpack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接