如何映射或嵌套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)

如果我在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)
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
循环的第一次迭代中完成的。