Python22: generator within a generator doesn't execute?

Started by
3 comments, last by thedustbustr 20 years, 8 months ago
from __future__ import generators from time import time threads=[] def sleep(n): print "In Sleep" t=time() while (t+n>time()): yield None def cutscene(): yield None #start on second pass print "\nEvil Death Knight: I will kill you now!" sleep(1.5) #t=time() #while (t+1.5>time()): yield None print "\nOur Hero: I shall fight you to the death. Bring it on!" sleep(2.5) #t=time() #while (t+2.5>time()): yield None print "\nEnd of cutscene." def ticker(): yield None #wait till second time through print "." t=time() while 1: while (t+1>time()): yield None t=time() print "." def scheduler(): global threads try: while 1: for thread in threads: thread.next() except StopIteration: pass if __name__=="__main__": threads.append(ticker()) threads.append(cutscene()) scheduler() ticker() prints out a period every second. cutscene() is a scripted game sequence. sleep(n) should cause a delay of n seconds (but doesn't block execution of the other microthreads (correct term?)). ticker() and cutscene() should be running simultaneously (in effect, at least). However, when run, the sleep function doesn't execute. It doesn't even print "In Sleep". It never gets called, even though I say sleep(1.5) in cutscene(). Run it for yourself if you don't believe me. Through my testing, any generator function will not execute if called from another generator function. Regular functions work fine. If I comment the sleep statements and uncomment the while loops, it runs as expected. Why doesn't the sleep() function execute? Thanks in advance. Dustin [edited by - thedustbustr on July 25, 2003 9:59:36 PM]
Advertisement
Hmmmm...Seems kind of strange. The only problem I can think off (coming from experience with C++) is a name conflict and Python typically (IMHO) has a way of prolonging this sort of debugging by ''failing silently'' as the documentation puts it. Anyway, I think your problem is Python thinks you are trying to ''spawn'' another microthread (this is the term I use) from WITHIN a microthread. I don''t believe this is allowed due to simple organizational issues, as its creating a thread inside of a thread that was created/yielded, etc. This is just a guess though. Pretty clever solution you figured out (from that last post) though . Good work!

Chris Pergrossi
< ctoan >
My Realm
Chris PergrossiMy Realm | "Good Morning, Dave"
Your call sleep(1.5) will return a generator, which you just discard. It will not call it.

Something along the lines of for x in sleep(1.5): yield x works much better.

If you want to get "native" Python microthread support, go check Stackless Python... Though I must admit, I find generators easier to use

Edit: I've also tweaked your scheduler to return once all threads are completed, instead of as soon as one threads completes.

# enumerate is a builtin in python 2.3# but you have to write your own in python 2.2def enumerate(collection):    index = 0    for value in collection:        yield index, value        index+=1def scheduler():    global threads    while threads:        for index,thread in enumerate(threads):            try:                thread.next()            except StopIteration:                threads[index] = None        threads = filter(None, threads)  


Yes, that means that, as it is, your scheduler runs forever.
However, nothing stops you from adding new threads while it is running.

[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]


[edited by - Fruny on July 25, 2003 6:52:59 AM]

[edited by - Fruny on July 25, 2003 6:59:36 AM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
So there is no way, save using stackless microthreads, to have a sleep function that ''just works''?

def xsleep(n):
t=time()
while (t+n>time()):
yield None

def sleep(n):
for i in xsleep(n): pass


This blocks execution, as if I had called time.sleep(). Why? Shouldn''t xsleep() yield to the scheduler to allow my other stuff to run?
xsleep() yields to sleep(), not to the scheduler. sleep() then calls xsleep() again, until that generator runs out. Only then does sleep() return - it is an ordinary function, not a generator.

In your original code, sleep() was a generator, not a function. Therefore the code you had written was just creating an instance of the generator and not iterating through it.

A generator yields to the function (or generator) that immediately called it. Just like return returns to the function (or generator) that called the function return was in.
In more general terms, how would your generator know to which function it must yield ... the interpreter's read-eval-print loop is itself a function, you know...
That's what continuations (in languages that implement them) are. Stackless 1.x used continuations, IIRC, Stackless 2.x does things differently, but I don't know the details as I have not really used either of them much.

Note that python's threads are a possible option too.

[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]


[edited by - Fruny on July 25, 2003 9:48:38 AM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan

This topic is closed to new replies.

Advertisement