【发布时间】:2009-09-03 23:12:33
【问题描述】:
如何在 Python 中制作重复生成器,例如 xrange?例如,如果我这样做:
>>> m = xrange(5)
>>> print list(m)
>>> print list(m)
我两次得到相同的结果——数字 0..4。但是,如果我尝试使用相同的产量:
>>> def myxrange(n):
... i = 0
... while i < n:
... yield i
... i += 1
>>> m = myxrange(5)
>>> print list(m)
>>> print list(m)
第二次尝试迭代 m 时,我什么也没得到——一个空列表。
有没有一种简单的方法来创建像 xrange 这样的重复生成器,或者生成器理解?我找到了a workaround on a Python tracker issue,它使用装饰器将生成器转换为迭代器。每次您开始使用它时都会重新启动,即使您上次没有使用所有值,就像 xrange 一样。我也想出了自己的装饰器,基于相同的想法,它实际上返回一个生成器,但可以在抛出 StopIteration 异常后重新启动:
@decorator.decorator
def eternal(genfunc, *args, **kwargs):
class _iterable:
iter = None
def __iter__(self): return self
def next(self, *nargs, **nkwargs):
self.iter = self.iter or genfunc(*args, **kwargs):
try:
return self.iter.next(*nargs, **nkwargs)
except StopIteration:
self.iter = None
raise
return _iterable()
有没有更好的方法来解决这个问题,只使用 yield 和/或生成器理解?还是 Python 内置的东西?所以我不需要推出自己的类和装饰器?
更新
comment by u0b34a0f6ae 指出了我误会的根源:
xrange(5) 不返回迭代器,它创建一个 xrange 对象。 xrange 对象可以多次迭代,就像字典一样。
我的“永恒”函数完全是错误的树,就像一个迭代器/生成器(__iter__ 返回自身)而不是一个集合/xrange(__iter__ 返回一个新迭代器)。
【问题讨论】:
-
小挑剔,但
xrange()不是生成器。type(xrange(4))!=type(myxrange(4)). -
我认为这不仅仅是一个小问题。这就是差异的全部原因。正如约翰指出的那样,可以通过重载的 iter 获得所需的行为。
-
与其他两个提议的实现相比(一个在您链接到的 Python 跟踪器问题中,另一个在 @JohnMillikin 的答案中),我在遵循您的实现代码时遇到了很多麻烦。特别是,难以弄清楚:(1)“@decorator.decorator”到底是什么意思。你能给它一个文档的链接吗? (2)一个用法示例将非常有帮助;特别是一个练习 args 和 nargs (3) 的例子,你能举例说明你的 StopIteration 处理如何增加价值吗?即您的实现成功但其他两个实现失败的示例。
-
@DonHatch 希望我刚刚添加到我的问题的更新解释了为什么我的实现代码难以理解(并且完全错误)。
标签: python