How to match or insert Python 2.7 function generators?

If I have a very simple (although possibly very complex) function generator in Python 2.7, for example:

def accumulator(): x = yield 0 while True: x += yield x 

What can be used, for example:

 >>> a = accumulator() >>> a.send(None) 0 >>> a.send(1) 1 >>> a.send(2) 3 >>> a.send(3) 6 

What will be a simple wrapper for another function generator that produces the same result except multiplying by 2? The above function generator is simple, but please assume that it is too complicated to copy-paste. I am trying something like:

 def doubler(): a = accumulator() a.send(None) y = yield 0 while True: y = 2 * a.send(yield y) 

Or, introducing something simpler:

 def doubler(): a = accumulator() a.send = lambda v: 2 * super(self).send(v) return a 

Both of them are terribly broken, so I will not pass syntax errors, but this can illustrate what I'm trying to do.

Ideally, I would like to get something like:

 >>> d = doubler() >>> d.send(None) 0 >>> d.send(1) 2 >>> d.send(2) 6 >>> d.send(3) 12 

The results are the same as the original, except for doubling.

I try to avoid duplication of a very complex function generator in order to create an identical result, except for scaling using a known factor.

The second generator will ultimately have a different input stream, so I cannot just use the result from the first generator and double it. I need a second independent generator to wrap the first.

The input stream is indefinite, so it is not possible to generate the entire sequence and then convert.

I seem to want to display or embed these function generators, but I'm not sure about the appropriate jargon, and so I haven’t been to Google anywhere.

+5
source share
4 answers

If you need the same interface as the coroutine (i.e. it has a send method), then the BrenBarn solution is probably as simple as it is. *

If you can have a slightly different interface, then a higher order function is even simpler:

 def factor_wrapper(coroutine, factor): next(coroutine) return lambda x, c=coroutine, f=factor: f * c.send(x) 

You would use it as follows:

 >>> a = accumulator() >>> a2 = factor_wrapper(a, 2) >>> print a2(1) 2 >>> print a2(2) 6 >>> print a2(3) 12 

* In fact, you can shave a few lines to make it only 4 lines, although do not reduce the complexity very much.

 def doubler(a): y = yield next(a) while True: y = yield (2 * a.send(y)) 

or even shorter ...

 def doubler(a, y=None): while True: y = yield 2 * a.send(y) 

Any of the above can be used as follows:

 >>> 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 
+2
source

I have not tried this, but something like this:

 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 

In addition, after Python 3.5, expanding this parameter from collections.abc.Container eliminates the need for __next__ , will also make it a proper generator (currently it does not support __throw__ , etc., but they are just templates).

Edit: Yes, this works:

 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 
+2
source

You just need to move yield beyond the expression that goes from y to a :

 def doubler(): a = accumulator() next(a) y = yield 0 while True: y = yield (2 * a.send(y)) 

Then:

 >>> a = accumulator() ... d = doubler() ... next(a) ... next(d) ... for i in range(10): ... print(a.send(i), d.send(i)) 0 0 1 2 3 6 6 12 10 20 15 30 21 42 28 56 36 72 45 90 
+2
source

I think this is what you want:

 def doubler(): a = accumulator() y = a.send(None) x = yield 0 while True: y = a.send(x) x = yield 2 * y 

This completes the implementation of accumulator completely, but you can alternatively make it visible and pass it as parameter a to double it.

0
source

Source: https://habr.com/ru/post/1232599/


All Articles