在一个类中传递Python回调函数

11

我必须尝试从非基于类的编码方式转换为基于类的编码方式,但是遇到了问题。optimize() 函数接受一个回调函数 mycallback()。 在非基于类的方法中,代码可以完美运行,但是当我将其移植到基于类的方法时,我收到了错误消息“mycallback()需要3个参数,但只提供了1个”。

在基于类的方法中传递回调函数的正确方式是什么?

(A) 非基于类的方法:

def mycallback(model, where):
    pass

model = Model()
model.optimize(mycallback)

(B) 基于类的方法:

class A:
    def __init__(self):
        self.model = Model()

    def solve(self):
        # Try method 1:
        self.model.optimize(self.mycallback())      <--- Error: mycallback() takes exactly 3 arguments (1 given)
        # Try method 2:
        # self.model.optimize(self.mycallback)  <--- Error:  Callback argument must be a function

    def mycallback(self, model, where):     
        pass

虽然这是一个关于将回调函数传递给Gurobi(一种优化求解器)内置函数的问题,但我认为它更是一个关于如何将类中定义的回调函数传递给Python中的另一个函数的更一般性问题。


方法2的错误:

   self.model.optimize(self.mycallback)  
   File "model.pxi", line 458, in gurobipy.Model.optimize      (../../src/python/gurobipy.c:34263)
   gurobipy.GurobiError: Callback argument must be a function

看起来可能是Gurobi API的问题。不知道是否会有任何Gurobi开发人员回复。


为什么即使你只是“pass”,也要有三个参数呢? - sshashank124
5
不需要调用 self.mycallback(),将其作为参数传递给self.model.optimize(self.mycallback) - bereal
请在使用方法2时发布完整的回溯错误消息。 - unutbu
5个回答

5

通常情况下,self.model.optimize(self.mycallback)应该可以工作(注意:mycallback后没有括号)。

如果代码序列化可调用对象,例如通过管道/套接字发送到另一个进程(甚至在不同的计算机上),则可能会失败:

from multiprocessing import Pool

class C:
    def method(self, i):
        return "called", i

if __name__=="__main__":
    print(Pool().map(C().method, range(10)))

它适用于最近的Python版本,其中方法是可取的。

或者,如果model.optimize()有一个错误,并且检查确切的函数类型而不是接受任何可调用对象,则可能会失败。


2

这个问题在gurobi 9.1中仍然存在。我发现一个简单的解决方法是将回调函数放在类的方法内,例如:

def solve(self):
    self.model.update()
       def lazyCallback(model, where):
       ...
    self.model.optimize(lazyCallback)

0

我的解决方案是:


import queue as Queue
class A:
    def start(self):
       Queue.listen(callback=received)

    @staticmethod
    def received(*args):
       print(args)

队列在另一个模块/文件中定义。


0

self.mycallback 的问题在于它是一个方法,而Gurobi的optimize方法实际上期望的是一个函数

有多种方法可以将self.mycallback变成一个函数。以下是一些示例:

一种方法是将mycallback变成静态方法:

@staticmethod
def mycallback(model, where): 
    pass

另一个方法是将该方法封装到一个函数中,如下所示:

self.model.optimize(lambda model, where: self.mycallback(model, where))    

0

看起来如果从对象中删除回调,那么它就可以工作了。您可以将其用作解决方法,直到您可以在类中使回调正常工作。也就是说,从类内部调用此行...

def solve(self):
    self.model.optimize(mycallback)

...在类外调用此函数。

def mycallback(self, model, where):     
    pass

并不优雅,但希望有开发者加入讨论。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接