将带有回调的函数变成 Python 生成器?

2023-12-26

Scipy 最小化函数(仅用作示例)可以选择在每个步骤添加回调函数。所以我可以做类似的事情,

def my_callback(x):
    print x
scipy.optimize.fmin(func, x0, callback=my_callback)

有没有办法使用回调函数创建 fmin 的生成器版本,以便我可以这样做,

for x in my_fmin(func,x0):
    print x

似乎通过产量和发送的某种组合是可能的,但我可以想到任何事情。


正如评论中指出的,您可以在新线程中使用Queue http://docs.python.org/library/queue.html。缺点是您仍然需要某种方式来访问最终结果(什么fmin最后返回)。我下面的示例使用可选回调来执行某些操作(另一个选项是也生成它,尽管您的调用代码必须区分迭代结果和最终结果):

from thread import start_new_thread
from Queue import Queue

def my_fmin(func, x0, end_callback=(lambda x:x), timeout=None):

    q = Queue() # fmin produces, the generator consumes
    job_done = object() # signals the processing is done

    # Producer
    def my_callback(x):
        q.put(x)
    def task():
        ret = scipy.optimize.fmin(func,x0,callback=my_callback)
        q.put(job_done)
        end_callback(ret) # "Returns" the result of the main call

    # Starts fmin in a new thread
    start_new_thread(task,())

    # Consumer
    while True:
        next_item = q.get(True,timeout) # Blocks until an input is available
        if next_item is job_done:
            break
        yield next_item

Update:要阻止下一次迭代的执行,直到消费者完成上一次迭代的处理,还需要使用task_done and join.

    # Producer
    def my_callback(x):
        q.put(x)
        q.join() # Blocks until task_done is called

    # Consumer
    while True:
        next_item = q.get(True,timeout) # Blocks until an input is available
        if next_item is job_done:
            break
        yield next_item
        q.task_done() # Unblocks the producer, so a new iteration can start

注意maxsize=1没有必要,因为在最后一个项目被消耗之前,不会将新项目添加到队列中。

更新2:另请注意,除非该生成器最终检索到所有项目,否则创建的线程将死锁(它将永远阻塞并且其资源永远不会被释放)。生产者正在队列上等待,并且由于它存储了对该队列的引用,因此即使消费者回收了,它也永远不会被 gc 回收。然后队列将变得不可访问,因此没有人能够释放锁。

如果可能的话,一个干净的解决方案是未知的(因为它取决于代替的特定函数)fmin)。可以使用以下方法解决此问题timeout,如果有的话,生产者会引发异常put阻塞时间过长:

    q = Queue(maxsize=1)

    # Producer
    def my_callback(x):
        q.put(x)
        q.put("dummy",True,timeout) # Blocks until the first result is retrieved
        q.join() # Blocks again until task_done is called

    # Consumer
    while True:
        next_item = q.get(True,timeout) # Blocks until an input is available
        q.task_done()                   # (one "task_done" per "get")
        if next_item is job_done:
            break
        yield next_item
        q.get() # Retrieves the "dummy" object (must be after yield)
        q.task_done() # Unblocks the producer, so a new iteration can start
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将带有回调的函数变成 Python 生成器? 的相关文章

随机推荐