Python passing callback function in a class

twfx picture twfx · Mar 29, 2014 · Viewed 10.3k times · Source

I have to try to move from the non-class-based coding style into class-based coding style, but facing an issue. The optimize() function takes a callback function mycallback(). The code works perfectly fine in Non-class-based method, but when I moved it to class-based method, I got an error "mycallback() takes exactly 3 arguments (1 given)".

What is the right way to pass a callback function in the class-based method?

(A) Non-class-based method:

def mycallback(model, where):
    pass

model = Model()
model.optimize(mycallback)

(B) Class-based method:

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

While this is a problem regarding passing a callback function to Gurobi's (an optimization solver) built-in function, I believe it is a more general question on how to pass a callback function defined in a class to another function in Python.


Error For method 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

Looks like it is likely to be Gurobi API issue. Wonder if any Gurobi dev will response.

Answer

jfs picture jfs · Mar 29, 2014

In general, self.model.optimize(self.mycallback) should work (note: no parens after mycallback).

It may fail if the code serializes the callable e.g., to be send via pipe/socket to another process (even on different machine):

from multiprocessing import Pool

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

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

It works on recent Python versions where methods are pickable.

Or it may fail if model.optimize() has a bug and check for the exact function type instead of accepting any callable.