在类中传递回调函数的Python

11 投票
5 回答
17038 浏览
提问于 2025-04-18 00:25

我正在尝试从非类的编码风格转向类的编码风格,但遇到了一个问题。我的优化函数 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 的开发者能回应一下。

5 个回答

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

虽然这样做不太优雅,但希望有开发者能给点建议。

2

这个问题在gurobi 9.1中仍然存在。我找到一个简单的解决办法,就是把回调函数放在你类中的一个方法里,比如:

def solve(self):
    self.model.update()
       def lazyCallback(model, where):
       ...
    self.model.optimize(lazyCallback)
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() 这个方法本身有bug,检查的是具体的函数类型,而不是接受任何可以调用的东西,那也可能会出错。

撰写回答