Reputation: 121
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)
Upvotes: 0
Views: 533
Reputation: 239443
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
Upvotes: 5
Reputation: 5181
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
Upvotes: 2
Reputation: 63978
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.
Upvotes: 4