Search code examples
pythonoopinstance-variablesnumba

Extracting and isolating a Python function, given an instance of a class


Suppose I am given an instance of a Python class. This class has a function, which I would like to extract and isolate from the class. The final function needs to have access to the values of the attributes from the original class, but otherwise it should not invoke the class at all.

My motivation is that I want to compile the function using jit from numba, but I do not want to compile the entire class using jitclass

As a concrete example, consider the following class. Ideally, I do not want to modify this code:

class Adder:
    """
    A minimal callable class with keyword arg
    """
    def __init__(self, num=4):
        self.num = num
    def add(self, val1, val2):
        return val1 + val2
    def add_default(self, val1, val2):
        return self.num + val1 + val2

I now instantiate these objects, and then try to compile their methods using numba. This works when the instance method does not use any of the object's attributes

from numba import jit

a = Adder(num=1)
print(a.add(7, 8)) # 15
add_fast = jit(a.add)
print(add_fast(7, 8)) # 15

However, this breaks when I try to use the method that requires a class attribute

from numba import jit

a = Adder(num=1)
print(a.add_default(7, 8)) # 16
add_default_fast = jit(a.add_default)
print(add_default_fast(7, 8)) # NotDefinedError: Variable 'self' is not defined.

I tried using a lambda function, but that still internally references the class and breaks jit. Ideally, I want to extract the instance method, as well as the values of all instance attributes, and build a new function that replaces all self. variables with their values from the instance attributes.

Is there a simple way to do this? I am hoping for a solution that does not involve refactoring the class.


Solution

  • Try this:

    def add_default(a,*args):
        return Adder.__dict__['add_default'](a,*args)
    
    add_default_fast = jit(add_default,forceobj=True)
    print(add_default_fast(a,7, 8)) # 16
    

    Instead of 'a' you can pass to the function any python object having a 'num' attribute

    (see also : Python: Hack to call a method on an object that isn't of its class for that dict trick which allows to explicitly specify the 'self' argument of the method)