简体   繁体   中英

Yield statement in generator causes skipped lines of code

I am writing a generator in python. It behaves strangely in that if I leave the yield statement where it is, then the print function at the top of the function is never called. If I remove the yield function, the print statement happens as expected. Currently this is the code.

def eachMovement(self):
        print "Each Movement..."

        if not self.isComplete():
            raise ValueError("DressageScore.eachMovement:  dressage score must be designated as complete.")

        for i in range(0, len(self.__iMovementScores)):
            yield (self.__iMovementScores[i], self.__iMovementComments[i])

Removing yield fixes this problem. What am I doing wrong here? For clarification, I am running Python 2.7.5. Please do not persuade me to do this a different way, my class requires the use of a generator. Thanks!

SOLUTION: Since I was doing Test Driven Development, I wrote the raise valueerror test before I wrote the rest of the function. After fully transforming eachMovement into a generator, my test case failed because it was not set up to test a generator. Silly mistake on my part. Here is the test case that now completes no problem. Thanks for all of your help

def test1300_910_EachMovementNotComplete(self):
    myGen = self.dsRider.eachMovement()

    self.assertRaises(ValueError, myGen.next)

If you simply invoke the generator function it will return generator object and it will not execute the function. You need to use next function on the generator object or someother function which can call next function on the generator object like list .

def myGen():
    print "Welcome"
    for i in range(10):
        yield i

print myGen()
print list(myGen())
print next(myGen())

Output

<generator object myGen at 0x7f1548366aa0>
Welcome
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Welcome
0

Normally generators are used for iteration (because once a generator is exhausted, it is not use. A new generator object has to be created for the next iteration.), so the best usage would be like this

for num in myGen():
    print num

Output

Welcome
0
1
2
3
4
5
6
7
8
9

I suspect you're simply calling my_obj.eachMovement() , correct?

Generators, unlike functions, do something called "lazy evaluation". Basically, Python will avoid running through the function until it absolutely needs to (which is useful, since it lets you write generators that return an infinite amount of elements, allowing the user to take only what they need, in contrast to normal functions, which would choke on similar inputs).

You need to do something like either:

>>> my_gen = my_obj.eachMovement()
>>> print next(my_gen)
Each Movement...
0  # whatever value

...or something like:

>>> for i in my_obj.eachMovement():
        print i
Each Movement...
0
1
# etc

Either method will force Python to actually evaluate the generator, since it requires the data in order to continue functioning.

yield in Python is a style of lazy evaluation , which in short means that successive elements of a sequence are only executed as they are needed.

For example, the builtin Python (2.7) function xrange is a generator, Python's term for a lazy evaluator.

In the same way you use xrange , you need to use your function:

for movement in eachMovement:
    # Do stuff with movement

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