如何映射或嵌套Python 2.7函数生成器?
如果我在Python 2.7中有一个非常简单(尽管可能非常复杂)的函数生成器,如下所示:如何映射或嵌套Python 2.7函数生成器?,python,python-2.7,generator,Python,Python 2.7,Generator,如果我在Python 2.7中有一个非常简单(尽管可能非常复杂)的函数生成器,如下所示: def accumulator(): x = yield 0 while True: x += yield x >>> a = accumulator() >>> a.send(None) 0 >>> a.send(1) 1 >>> a.send(2) 3 >>> a.send(3)
def accumulator():
x = yield 0
while True:
x += yield x
>>> a = accumulator()
>>> a.send(None)
0
>>> a.send(1)
1
>>> a.send(2)
3
>>> a.send(3)
6
可以这样使用:
def accumulator():
x = yield 0
while True:
x += yield x
>>> a = accumulator()
>>> a.send(None)
0
>>> a.send(1)
1
>>> a.send(2)
3
>>> a.send(3)
6
对于产生相同结果的另一个函数生成器,除了乘以2外,什么是简单的包装器?上面的函数生成器很简单,但请假定它太复杂,无法复制粘贴。我在尝试一些东西,比如:
def doubler():
a = accumulator()
a.send(None)
y = yield 0
while True:
y = 2 * a.send(yield y)
>>> d = doubler()
>>> d.send(None)
0
>>> d.send(1)
2
>>> d.send(2)
6
>>> d.send(3)
12
或者,想象一些更简单的事情:
def doubler():
a = accumulator()
a.send = lambda v: 2 * super(self).send(v)
return a
这两个都是非常糟糕的,所以我不会分享语法错误,但它可以说明我正在尝试做什么
理想情况下,我想得到一些东西,比如:
def doubler():
a = accumulator()
a.send(None)
y = yield 0
while True:
y = 2 * a.send(yield y)
>>> d = doubler()
>>> d.send(None)
0
>>> d.send(1)
2
>>> d.send(2)
6
>>> d.send(3)
12
结果与原始结果完全相同,只是增加了一倍
我试图避免复制一个非常复杂的函数生成器来创建相同的结果,除了按已知因子进行缩放
第二个生成器最终将有一个不同的输入流,因此我不能只使用第一个生成器的结果并将其加倍。我需要第二个独立的发电机,把第一个包起来
输入流是不确定的,因此不可能生成整个序列然后进行转换
似乎我想映射或嵌套这些函数生成器,但我不确定合适的术语,因此我在谷歌一无所获。我没有尝试过这一点,但大致如下:
class Doubler:
def __init__(self, g):
self.g = g()
def __next__(self):
return self.send(None)
def send(self, val):
return self.g.send(val)*2
此外,在Python3.5之后,从集合.abc.Container扩展它将消除对\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
编辑:是的,这是有效的:
In [1]: %paste
def accumulator():
x = yield 0
while True:
x += yield x
## -- End pasted text --
In [2]: %paste
class Doubler:
def __init__(self, g):
self.g = g()
def __next__(self):
return self.send(None)
def send(self, val):
return self.g.send(val)*2
## -- End pasted text --
In [3]: d = Doubler(accumulator)
In [4]: d.send(None)
Out[4]: 0
In [5]: d.send(1)
Out[5]: 2
In [6]: d.send(2)
Out[6]: 6
In [7]: d.send(3)
Out[7]: 12
只需将yield
移到传递y
到a
的表达式之外:
def doubler():
a = accumulator()
next(a)
y = yield 0
while True:
y = yield (2 * a.send(y))
然后:
我想这就是你想要的:
def doubler():
a = accumulator()
y = a.send(None)
x = yield 0
while True:
y = a.send(x)
x = yield 2 * y
这完全封装了累加器
实现,但您也可以将其作为参数a
传递给doubler。如果您需要与协同程序具有相同的接口(即具有发送
方法),那么BrenBarn的解决方案可能就简单多了*
如果可以使用稍有不同的接口,则更高阶函数更简单:
def factor_wrapper(coroutine, factor):
next(coroutine)
return lambda x, c=coroutine, f=factor: f * c.send(x)
您将按如下方式使用它:
>>> a = accumulator()
>>> a2 = factor_wrapper(a, 2)
>>> print a2(1)
2
>>> print a2(2)
6
>>> print a2(3)
12
>>> a = accumulator()
>>> a2 = doubler(a)
>>> print a2.send(None) # Alternatively next(a2)
0
>>> print a2.send(1)
2
>>> print a2.send(2)
6
>>> print a2.send(3)
12
*事实上,你可以把几行代码删去,使其总共4行,虽然这并不能真正降低复杂度
def doubler(a):
y = yield next(a)
while True:
y = yield (2 * a.send(y))
甚至更短
def doubler(a, y=None):
while True:
y = yield 2 * a.send(y)
上述任一项可按如下方式使用:
>>> a = accumulator()
>>> a2 = factor_wrapper(a, 2)
>>> print a2(1)
2
>>> print a2(2)
6
>>> print a2(3)
12
>>> a = accumulator()
>>> a2 = doubler(a)
>>> print a2.send(None) # Alternatively next(a2)
0
>>> print a2.send(1)
2
>>> print a2.send(2)
6
>>> print a2.send(3)
12
您是否遇到语法错误?前5个元素的预期结果是什么?有人能指出一个文档,我可以从中了解如何在生成器中使用该发送方法吗?@Ja8zyjits@Ja8zyjits请参阅David Beazley的演示幻灯片:非常好。我希望有一个不需要类包装器的更简单的解决方案,但我会一直使用它,直到它出现。谢谢!:)谢谢这更接近了,但仍然感觉应该有一个更简单的解决方案,它甚至不需要我创建第二个累加变量y。难道没有办法简单地将一个生成器传递给另一个生成器,从而缩放结果吗?我希望在第二个函数生成器def行旁边有1或2行。@Trevor:如果您需要能够将值发送到倍增器,我认为您无法获得更简单的解决方案。使用send
时,yield
承担双重角色,既接受传入的值,也接受传出的值。如果在中发送
值,它们将成为函数体中的屈服
表达式的结果。但是,您还需要yield
来返回加倍的结果。因此,您需要在函数中做一些工作来“传递”传入的值和输出的值。您也不能使用yield from
,因为这不允许您在退出时进行加倍。@Trevor:顺便说一句,您可以通过将第二行和第三行合并为yield next(a)
,稍微压缩它,这也有一个优点,即它将产生包装累加器的第一个值(而不是在第一次迭代中显式地生成0)。谢谢!我使用了您的“更简短”版本,如:def doubler(a=accumulator(),y=None):虽然为True:y=yield 2*a.send(y)
这种方法的一个警告是,您不能仅仅通过执行类似于a2=doubler();a3=doubler()的操作来创建两个doubler
因为两者都将尝试使用相同的生成器对象,第二个实例将引发TypeError。另一方面,您可以执行a2=doubler();a3=doubler(acculator())
,其中a2和a3将使用不同的生成器。True。在我的生产版本中,我在函数中实例化了嵌套累加器,而不是共享的*args位置。我仅使用此“最短”版本来证明概念……再次感谢!…示例:def doubler(func_gen,y=None):g=func_gen();g.send(None);while True:y=yield 2*g.send(y)
没问题!只要对细节一丝不苟,就不需要执行g.send(None)
,因为这是在while True
循环的第一次迭代中完成的。