python generator "send" function purpose?
Can someone give me an example of why the "send" function associated with Python generator function exists? I fully understand the yield function. However, the send function is confusing to me. The documentation on this method is convoluted:
Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value.
What does that mean? I thought value was the input to the function? The phrase "The send() method returns the next value yielded by the generator" seems to be also the exact purpose of the yield function; yield returns the next value yielded by the generator...
Can someone give me an example of a generator utilizing send that accomplishes something yield cannot?
It's used to send values into a generator that just yielded. Here is an artificial (non-useful) explanatory example:
>>> def double_inputs(): ... while True: ... x = yield ... yield x * 2 ... >>> gen = double_inputs() >>> next(gen) # run up to the first yield >>> gen.send(10) # goes into 'x' variable 20 >>> next(gen) # run up to the next yield >>> gen.send(6) # goes into 'x' again 12 >>> next(gen) # run up to the next yield >>> gen.send(94.3) # goes into 'x' again 188.5999999999999
You can't do this just with
As to why it's useful, one of the best use cases I've seen is Twisted's
@defer.inlineCallbacks. Essentially it allows you to write a function like this:
@defer.inlineCallbacks def doStuff(): result = yield takesTwoSeconds() nextResult = yield takesTenSeconds(result * 10) defer.returnValue(nextResult / 10)
What happens is that
takesTwoSeconds() returns a
Deferred, which is a value promising a value will be computed later. Twisted can run the computation in another thread. When the computation is done, it passes it into the deferred, and the value then gets sent back to the
doStuff() function. Thus the
doStuff() can end up looking more or less like a normal procedural function, except it can be doing all sorts of computations & callbacks etc. The alternative before this functionality would be to do something like:
def doStuff(): returnDeferred = defer.Deferred() def gotNextResult(nextResult): returnDeferred.callback(nextResult / 10) def gotResult(result): takesTenSeconds(result * 10).addCallback(gotNextResult) takesTwoSeconds().addCallback(gotResult) return returnDeferred
It's a lot more convoluted and unwieldy.
★ Back to homepage or read more recommendations: