在类中传递回调函数的Python
我正在尝试从非类的编码风格转向类的编码风格,但遇到了一个问题。我的优化函数 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 个回答
我来分享一下我的看法:
import queue as Queue
class A:
def start(self):
Queue.listen(callback=received)
@staticmethod
def received(*args):
print(args)
队列是在另一个模块/文件中定义的。
问题在于 self.mycallback
是一个 方法,而 Gurobi 的 optimize
方法其实需要的是一个 函数。
有好几种方法可以把 self.mycallback
转换成一个函数。下面是一些例子:
一种方法是把 mycallback
变成一个静态方法:
@staticmethod
def mycallback(model, where):
pass
另一种方法是把这个方法包裹成一个函数,像这样:
self.model.optimize(lambda model, where: self.mycallback(model, where))
看起来如果你把对象里的回调去掉,它就能正常工作了。在你能在类里面搞定回调之前,可以先用这个方法应急。也就是说,从类里面调用这一行...
def solve(self):
self.model.optimize(mycallback)
...去调用这个类外面的函数。
def mycallback(self, model, where):
pass
虽然这样做不太优雅,但希望有开发者能给点建议。
这个问题在gurobi 9.1
中仍然存在。我找到一个简单的解决办法,就是把回调函数放在你类中的一个方法里,比如:
def solve(self):
self.model.update()
def lazyCallback(model, where):
...
self.model.optimize(lazyCallback)
一般来说,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,检查的是具体的函数类型,而不是接受任何可以调用的东西,那也可能会出错。