Определение метода из строки в Python 3 и ссылка на метод

У меня есть необходимость позволить пользователю определить функцию, которая обрабатывает данные в объекте (мудрость и последствия для безопасности в этом были подробно обсуждены в другом вопросе и будут просто дублирующими комментариями здесь.)

Я хотел бы, чтобы функция действовала так же, как любой другой метод. То есть

def my_method(self):...

Будет вызван с:

obj_handle.my_method()

Я почти достиг этого ниже, за исключением того, что функция, результаты которой должны быть явно переданы self в качестве аргумента, вместо получения его в качестве первого аргумента, как это типично для метода.

Вы можете увидеть это в собственности p где у меня странный self.process(self) вызов.

Я представляю, что мне нужно что-то предоставить exec это как globals() словарь, но я не уверен в нескольких вещах:

  1. Это правильно?
  2. Что эквивалентно 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

2 ответа

Решение

Если вы хотите работать с экземплярами, а не с классами:

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)

Ответ на комментарий @juanpa.arrivillaga выше:

Установите функцию в классе, если вы хотите, чтобы ее протокол дескриптора работал, и связывали экземпляр с экземпляром при вызове экземпляра. Так что одно решение - просто сделать ваш set_process методом класса. - 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
Другие вопросы по тегам