Wednesday, March 30, 2016

Quick Tip: Understanding the Yield Keyword in Python

When we call a function in Python, the function will normally start working until it encounters a return, exception, or reaches its end – after which it returns control back to the caller. Whenever you call that function again, the process will start from scratch!

Say that you asked a person to track the red cars on the road. The person will keep getting a question asking them if they spotted a red car or not, and the person in turn would answer with either ‘yes’ or ‘no’. If the person answered ‘yes’, the number of times the red car was spotted will increase.

Let’s see how we can do this in Python:

import time

def red_cars(answer):
    n = 0
    while True:
        if answer == 'yes':
            n = n + 1
            return n
        else:
            return n

stop = time.time() + 5 * 60
while time.time() < stop:
    answer = raw_input('Did you spot a red car on the road? ("yes" or "no"): ')
    times = red_cars(answer)
    print 'You have spotted ' + str(times) + ' cars so far!'

If you run the program, what do you notice? Did you notice that the number of times for the ‘yes’ answer is always capped at 1, and when you answer ‘no’ the number of times gets 0 regardless of answering ‘yes’ before?

Here is where Python’s yield keyword comes into play. yield is a means by which we temporarily hand control to the caller, and expect to continue from the point at which control has been handed over.

Before giving the solution to the above example, let me demonstrate a very simple example to better illustrate how yield works.

Say we have the following simple Python script:

def step_by_step():
    return 'step 1'
    return 'step 2'
    return 'step 3'
    
step = step_by_step()
for i in range (3):
    print step

If you run the script, you will get the following output:

step 1
step 1
step 1

Now, if we use yield instead, as follows:

def step_by_step():
    yield 'step 1'
    yield 'step 2'
    yield 'step 3'
    
step = step_by_step()
for i in range (3):
    print step.next()

The output would be as follows:


step 1
step 2
step 3

As you can see, we were able to create a series of values, as for each call the function continues from the point where it yields a value. This type of function is called a generator. Such function creates a generator iterator, as with each call to the method next() we move to the next yield statement.

If we come back to our main example (red cars), it can be written as follows to perform the required task:

import time

def red_cars(answer = None):
    n = 0
    while True:
        if answer=="yes":
            n = n + 1
            answer = yield n
        else:
            answer = yield n

car_color = red_cars()
car_color.next()

stop = time.time() + 5 * 60
while time.time() < stop:
    answer = raw_input('Did you spot a red car on the road? ("yes" or "no"): ')
    print 'You have spotted ' + str(car_color.send(answer)) + ' cars so far!'

Thus, as we can see, yield is deemed important when we are interested in resuming execution at the last point where the function (generator) exited, and where we are also interested in keeping the values of local variables between the different calls – unlike normal functions, where such values are destroyed when exiting the function.

Continue reading %Quick Tip: Understanding the Yield Keyword in Python%


by A. Hasan via SitePoint

No comments:

Post a Comment