我有一个生成器,每次运行需要很长时间才能运行。是否有标准的方法让它产生一个值,然后在等待再次调用时生成下一个值?
每次在gui中按下按钮时都会调用生成器,并且每次按下按钮后都会要求用户考虑结果。
编辑:解决方法可能是:
def initialize():
res = next.gen()
def btn_callback()
display(res)
res = next.gen()
if not res:
return
答案 0 :(得分:7)
没有。生成器不是异步的。这不是多处理。
如果您想避免等待计算,您应该使用multiprocessing
包,以便独立的流程可以进行昂贵的计算。
您需要一个单独的过程来计算和排队结果。
您的“生成器”可以简单地将可用结果出列。
答案 1 :(得分:6)
如果我想做一些像你的解决方法,我会写一个这样的课:
class PrefetchedGenerator(object):
def __init__(self, generator):
self._data = generator.next()
self._generator = generator
self._ready = True
def next(self):
if not self._ready:
self.prefetch()
self._ready = False
return self._data
def prefetch(self):
if not self._ready:
self._data = self._generator.next()
self._ready = True
它比你的版本更复杂,因为我这样做是因为它处理不会调用prefetch或调用prefetch太多次。基本的想法是,当你想要下一个项目时,你可以调用.next()。当你有“时间”杀死时,你可以调用prefetch。
你的另一个选择是一个线程..
class BackgroundGenerator(threading.Thread):
def __init__(self, generator):
threading.Thread.__init__(self)
self.queue = Queue.Queue(1)
self.generator = generator
self.daemon = True
self.start()
def run(self):
for item in self.generator:
self.queue.put(item)
self.queue.put(None)
def next(self):
next_item = self.queue.get()
if next_item is None:
raise StopIteration
return next_item
这将与您的主应用程序分开运行。无论获取每次迭代需要多长时间,您的GUI都应保持响应。
答案 2 :(得分:1)
你绝对可以使用生成器执行此操作,只需创建生成器,以便每个next
调用在获取下一个值之间交替,并通过放入多个yield
语句来返回它。这是一个例子:
import itertools, time
def quick_gen():
counter = itertools.count().next
def long_running_func():
time.sleep(2)
return counter()
while True:
x = long_running_func()
yield
yield x
>>> itr = quick_gen()
>>> itr.next() # setup call, takes two seconds
>>> itr.next() # returns immediately
0
>>> itr.next() # setup call, takes two seconds
>>> itr.next() # returns immediately
1
请注意,生成器不会自动执行处理以获取下一个值,调用者可以为每个值调用next
两次。对于您的用例,您可以在设置时调用next
一次,然后每次用户单击该按钮时,您将显示生成的下一个值,然后再次调用next
进行预取。 / p>
答案 3 :(得分:0)
我是在追求类似的东西。我希望yield能够快速返回一个值(如果可能的话),而后台线程处理下一个,下一个。
import Queue
import time
import threading
class MyGen():
def __init__(self):
self.queue = Queue.Queue()
# Put a first element into the queue, and initialize our thread
self.i = 1
self.t = threading.Thread(target=self.worker, args=(self.queue, self.i))
self.t.start()
def __iter__(self):
return self
def worker(self, queue, i):
time.sleep(1) # Take a while to process
queue.put(i**2)
def __del__(self):
self.stop()
def stop(self):
while True: # Flush the queue
try:
self.queue.get(False)
except Queue.Empty:
break
self.t.join()
def next(self):
# Start a thread to compute the next next.
self.t.join()
self.i += 1
self.t = threading.Thread(target=self.worker, args=(self.queue, self.i))
self.t.start()
# Now deliver the already-queued element
while True:
try:
print "request at", time.time()
obj = self.queue.get(False)
self.queue.task_done()
return obj
except Queue.Empty:
pass
time.sleep(.001)
if __name__ == '__main__':
f = MyGen()
for i in range(5):
# time.sleep(2) # Comment out to get items as they are ready
print "*********"
print f.next()
print "returned at", time.time()
上面的代码给出了以下结果:
*********
request at 1342462505.96
1
returned at 1342462505.96
*********
request at 1342462506.96
4
returned at 1342462506.96
*********
request at 1342462507.96
9
returned at 1342462507.96
*********
request at 1342462508.96
16
returned at 1342462508.96
*********
request at 1342462509.96
25
returned at 1342462509.96