ziva
ziva

Reputation: 23

Python coroutines gives unknown None on yield

i'm trying to build a pipeline of some math operations, based on user input and try to print the acumulation result for this operations. For example the input would be a list of operations, then the number of numeric inputs and then numbers like this:

[square, accumulate]
3
1
2
3

This should return something like:

1
5
14

First it prints 1 as 1*1, then it adds up that 1 to the result of 2*2 giving 5, then adds it to the 3*3 giving 14. But there's something wrong with my approach, the second number input always turns into a None value and I don't know why. I'm stuck getting:

1
None
10

Any ideas? Here is my code:

import math
import os
import random
import re
import sys

def printer():
    while True:
        x = yield
        print(x)

def co_generator(n):
    for _ in range(n):
        x = int(input())
        yield x

def get_root():
    while True:
        number = (yield)
        yield math.floor(math.sqrt(number))

def get_square():
    number = 0
    while True:
        number = (yield)
        yield number**2

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

def operations_pipeline(numbers, operations, print_acum):
    for num in numbers:
        for i, w in enumerate(operations):
            num = w.send(num)
        print_acum.send(num)
    for operation in operations:
        operation.close()
    print_acum.close()

if __name__ == '__main__':
    order = input().strip()
    n = int(input())

    numbers = co_generator(n)

    print_acum = printer()
    next(print_acum)

    root = get_root()
    next(root)

    accumulate = accumulator()
    next(accumulate)

    square = get_square()
    next(square)

    operations_pipeline(numbers, eval(order), print_acum)

Upvotes: 2

Views: 2971

Answers (3)

samarth karan
samarth karan

Reputation: 21

def rooter():
    number=0
    while True:
        number= yield math.floor(math.sqrt(number))
    

def squarer():
    number=0
    while True:
        number= yield number**2

yield will give none try this way

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

Upvotes: 2

user2357112
user2357112

Reputation: 280465

You're writing your code as if (yield) receives a value and yield whatever sends one. That's not how it works. All yields both send a value and receive one.

When a generator executes a yield, it does so in two phases. First, the argument of the yield becomes the return value of the current next or send call, and the generator suspends execution. If there is no argument, None is used. That's where your Nones are coming from

Second, when another next or send is executed, the generator unsuspends, and the send argument (or None if next was used) becomes the value of the yield expression.

You're trying to use one yield to receive a send argument and another to set send's return value. You need to use a single yield to set a send return value and receive the next send's argument. For example,

def get_square():
    number = 0
    while True:
        number = yield number**2

Alternatively, if you want to use separate yields to send and receive values on the generator's end, then you need to use separate send (or next) calls to receive and send values on the other end, and ignore the Nones. For example,

w.send(num)
num = next(w)

instead of num = w.send(num), so operations_pipeline would look like

def operations_pipeline(numbers, operations, print_acum):
    for num in numbers:
        for w in operations:
            w.send(num)
            num = next(w)
        print_acum.send(num)
    for operation in operations:
        operation.close()
    print_acum.close()

Upvotes: 3

totalhack
totalhack

Reputation: 2598

I believe you need to add a call to next in your operations_pipeline code here:

def operations_pipeline(numbers, operations, print_acum):
    for num in numbers:
        for i, w in enumerate(operations):
            num = w.send(num)
            next(w) # <<<--- this

Without that I don't believe it was ever getting back into get_square the second time, assuming your first input was [square, accumulate].

Upvotes: 0

Related Questions