从 Python 3 中的字符串定义方法并引用为方法

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

我需要允许用户定义一个函数来处理对象中的数据(这方面的智慧和安全影响已经在另一个问题中进行了详细讨论,这里只是重复的评论。)

我希望该函数的作用与任何其他方法一样。即

def my_method(self):...

将被调用:

obj_handle.my_method()

我几乎在下面实现了这一点,除了结果函数需要显式传递 self 作为参数,而不是像通常的方法那样将其作为第一个参数接收。

你可以在 属性 p 中看到这个,我有奇怪的 self.process(self) 电话。

我想我需要向 exec 提供一些类似于 globals() 字典的东西,但我不确定有几件事:

  1. 这是正确的吗?
  2. class中的globals()相当于什么?
  3. 这是否解决了问题?如果不是,我需要做什么?

所以问题是,如何获得一个 exec() 定义的函数作为对象的方法?

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

在上面@juanpa.arrivillaga的评论中回答:

如果您希望其描述符协议起作用并在实例上调用时绑定类型实例,请在 class 上设置该函数。因此,一个解决方案就是让您的 set_process 成为 class 方法。 – juanpa.arrivillaga 1 小时前

工作结果

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

如果你想对实例进行操作而不是类:

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)