Search code examples
pythonpython-3.xpython-exec

Defining a Method from A String in Python 3 and Referencing As Method


I have a need to allow the user to define a function that processes data in an object (the wisdom and security implications in this have been discussed at length in another question and would just be duplicate comments here.)

I'd like the function to act just like any other method. That is

def my_method(self):...

Would be invoked with:

obj_handle.my_method()

I almost have this achieved below except that the function that results need to be explicitly passed self as an argument, rather than receiving it as the first argument as is typical for a method.

You can see this in property p where I have the odd self.process(self) call.

I imagine that I need to provide something to exec that is like the globals() dictionary, but I'm not certain of several things:

  1. Is this correct?
  2. What is the equivalent of globals() in a class?
  3. Does this solve the problem? If not what do I need to do?

So the question is, how do I get an exec() defined function to act as an object's method?

class test:
   def __init__(self, a, b):
       self.a=a
       self.b=b

   @property
   def p(self):
       return self.process(self)

   def set_process(self,program):
       func_dict={}
       proc_fun = exec(program,func_dict)
       setattr(self,'process',func_dict['process'])

   def process(self):
       return self.a+self.b

t=test(1,2)
prog = '''\
def process(self):
    return self.a * self.b
'''

t.set_process(prog)
t.p

Solution

  • If you want to operate on instances rather than classes:

    import types
    
    class Test:
    
        def __init__(self, a, b):
            self.a = a
            self.b = b
    
        @property
        def p(self):
            return self.process()
    
        def set_process(self, program):
            d = dict()
            exec(program, d)
            self.process = types.MethodType(d["process"], self)
    
        def process(self):
            return self.a + self.b
    
    prog = '''\
    def process(self):
        return self.a * self.b
    '''
    
    t = Test(1, 2)
    t.set_process(prog)
    print(t.p)
    
    t = Test(1, 2)
    print(t.p)