我认为这可行:
import itertools as it
def g(f, x):
return it.chain([x],(setattr(g, 'x', f(getattr(g, 'x', x))) or getattr(g, 'x') for _ in it.count()))
def f(x):
return x + 1
gen = g(f, 1)
print next(gen)
print next(gen)
print next(gen)
print next(gen)
当然,它依赖于一些粗略的行为,我实际上向函数本身添加了一个属性来保持状态。基本上,这个函数只会在你第一次调用它时起作用。在那之后,所有的赌注都被取消了。
如果我们想放宽这个限制,我们可以使用临时命名空间。问题是要获得一个临时命名空间,我们需要一个唯一的类实例(或类,但一个实例更干净,只需要一组额外的括号)。为了在一行中实现这一点,我们需要创建一个新的内联函数并将其用作默认参数:
import itertools as it
def g(f, x):
return (lambda f, x, ns=type('foo', (object,), {})(): \
it.chain([x],
(setattr(ns, 'x', f(getattr(ns, 'x', x))) or getattr(ns, 'x')
for _ in it.count()))
)(f, x)
def f(x):
return x + 1
gen = g(f, 1)
print next(gen) == 1
print next(gen) == 2
print next(gen) == 3
print next(gen) == 4
print "first worked?"
gen2 = g(f, 2)
print next(gen2) == 2
print next(gen2) == 3
print next(gen2) == 4
为了便于阅读,我将它分成几行,但它本质上是 1 行。
没有任何导入的版本
(也是我认为最强大的一个)。
def g(f, x):
return iter(lambda f=f, x=x, ns=type('foo', (object,), {'x':x}): ((getattr(ns, 'x'),setattr(ns, 'x', f(getattr(ns, 'x'))))[0]), object())
这里的一个技巧和以前一样。我们创建一个带有可变默认参数的 lambda 函数来保持状态。在函数内部,我们构建了一个元组。第一项是我们真正想要的,第二项是用于更新状态的setattr 函数的返回值。为了摆脱itertools.chain,我们将命名空间的初始值设置为x,这样类就已经初始化为具有起始状态。第二个技巧是我们使用iter 的双参数形式来摆脱之前仅用于创建无限迭代的it.count()。 iter 不断调用您作为第一个参数提供的函数,直到返回值等于第二个参数。然而,由于我的第二个参数是object 的一个实例,所以从我们的函数返回的任何内容都不会等于它,因此我们有效地创建了一个没有itertools 或yield 的无限迭代!想想看,我相信最后一个版本也是最强大的。以前的版本有一个错误,它们依赖于 f 的返回值的真实性。我认为如果f 返回0,他们可能会失败。最后一个版本修复了这个错误。