Python生成器预取?
我有一个生成器,每次运行都需要很长时间。有没有什么标准的方法可以让它先返回一个值,然后在等待再次被调用时继续生成下一个值?
这个生成器会在图形界面中每次按下按钮时被调用,用户需要在每次按下按钮后考虑结果。
补充说明:一个解决方法可能是:
def initialize():
res = next.gen()
def btn_callback()
display(res)
res = next.gen()
if not res:
return
4 个回答
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
来预先获取下一个值。
8
不,生成器并不是异步的。这和多进程不一样。
如果你想避免等待计算结果,你应该使用 multiprocessing
这个包,这样就可以让一个独立的进程来处理那些耗时的计算。
你需要一个单独的进程来进行计算并把结果放入队列。
然后你的“生成器”就可以简单地从队列中取出可用的结果。
11
如果我想做你提到的那种解决办法,我会写一个这样的类:
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)或者调用预取太多次的问题。基本的思路是,当你想要下一个项目时,就调用 .next()。而当你有“闲暇时间”时,就调用预取。
你还有一个选择,就是使用线程……
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
这样的话,它会和你的主应用程序分开运行。无论每次获取数据需要多长时间,你的界面都应该保持响应,不会卡住。