0

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
Ray Salemi
  • 5,247
  • 4
  • 30
  • 63
  • 2
    Set the function **on the class** if you want its descriptor protocol to work and bind tye instance when called on an instance. So one solution just make your `set_process` a classmethod. – juanpa.arrivillaga Jul 19 '18 at 15:23

2 Answers2

3

Answered in @juanpa.arrivillaga's comment above:

Set the function on the class if you want its descriptor protocol to work and bind tye instance when called on an instance. So one solution just make your set_process a classmethod. – juanpa.arrivillaga 1 hour ago

Working result

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

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

    @classmethod
    def set_process(cls,program):
        func_dict={}
        proc_fun = exec(program,func_dict)
        setattr(cls,'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
'''

test.set_process(prog)
t.p
Ray Salemi
  • 5,247
  • 4
  • 30
  • 63
1

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)
rsandwick3
  • 566
  • 4
  • 7
  • 1
    How does this actually work? Does `types.MethodType()` create an object of type `MethodType` that somehow handles the `self` issue? – Ray Salemi Jul 20 '18 at 15:18