简体   繁体   中英

Sending data to a generator in Python

My objective is to get my head around the concept of sending data to a Python generator. Below is my code snippet and the unexpected output:

def spam():
    while True:
        received = yield
        print("Message received from generator function:", received)
        yield received


if __name__ == "__main__":
    s = spam()
    print(s)
    next(s)
    names = ["Bob", "Nancy", "Daniela", "Martin"]
    for name in names:
        print(s.send(name))

Output :

7tvmb228:Own subhayan.bhattachary$ python test_generators.py 
<generator object spam at 0x10873aa20>
Message received from generator function: Bob
Bob
None
Message received from generator function: Daniela
Daniela
None

Can someone please point me out where is the None values coming from and how to solve that . My assumption is somehow that values do not get passed to the function. But why is that the case. Where am i going wrong .

Thanks for any answers in advance.

The None values are coming from received = yield , which essentially means you are yielding None value. You need to have only one yield in your spam() function:

def spam():
    received = None
    while True:
        received = yield received
        print("Message received from generator function:", received)


if __name__ == "__main__":
    s = spam()
    print(s)
    next(s)
    names = ["Bob", "Nancy", "Daniela", "Martin"]
    for name in names:
        print(s.send(name))

Prints:

<generator object spam at 0x7f1518c8f9e8>
Message received from generator function: Bob
Bob
Message received from generator function: Nancy
Nancy
Message received from generator function: Daniela
Daniela
Message received from generator function: Martin
Martin

According to the docs:

Because generator-iterators begin execution at the top of the generator's function body, there is no yield expression to receive a value when the generator has just been created. Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started, and a TypeError is raised if this occurs (presumably due to a logic error of some kind). Thus, before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield expression.

So you may change your code like this:

def spam():
    while True:
        received = yield
        print("Message received from generator function:", received)
        yield received


if __name__ == "__main__":
    s = spam()
    print(s)
    names = ["Bob", "Nancy", "Daniela", "Martin"]
    for name in names:
        next(s)
        print(s.send(name))

The documentation states:

Values are sent into a generator by calling its send(value) method. This method resumes the generator's code and the yield expression returns the specified value. If the regular __next__() method is called, the yield returns None .

Solution

Put an additional next(s) after each send to 'flush' the None results.

def spam():
    while True:
        received = yield
        print("Message received from generator function:", received)
        yield received


if __name__ == "__main__":
    s = spam()
    print(s)
    next(s)
    names = ["Bob", "Nancy", "Daniela", "Martin"]
    for name in names:
        print(s.send(name))
        next(s)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM