Подтвердить что ты не робот

Создание динамического/времени выполнения (генерация кода) в Python

Мне нужно сгенерировать код для метода во время выполнения. Важно иметь возможность запускать произвольный код и иметь docstring.

Я придумал решение, объединяющее exec и setattr, здесь пример фиктивного примера:

class Viking(object):
    def __init__(self):
        code = '''
            def dynamo(self, arg):
                """ dynamo a dynamic method!
                """
                self.weight += 1
                return arg * self.weight
            '''
        self.weight = 50

        d = {}
        exec code.strip() in d
        setattr(self.__class__, 'dynamo', d['dynamo'])


if __name__ == "__main__":
    v = Viking()
    print v.dynamo(10)
    print v.dynamo(10)
    print v.dynamo.__doc__

Есть ли лучший/более безопасный/более идиоматический способ достижения того же результата?

4b9b3361

Ответ 1

На основе кода Theran, но распространяя его на методы в классах:



class Dynamo(object):
    pass

def add_dynamo(cls,i):
    def innerdynamo(self):
        print "in dynamo %d" % i
    innerdynamo.__doc__ = "docstring for dynamo%d" % i
    innerdynamo.__name__ = "dynamo%d" % i
    setattr(cls,innerdynamo.__name__,innerdynamo)

for i in range(2):
    add_dynamo(Dynamo, i)

d=Dynamo()
d.dynamo0()
d.dynamo1()


Для печати:


in dynamo 0
in dynamo 1

Ответ 2

Функции docstrings и names являются изменяемыми свойствами. Вы можете делать все, что хотите во внутренней функции, или даже иметь несколько версий внутренней функции, которую выбирает makedynamo(). Не нужно создавать какой-либо код из строк.

Вот фрагмент из интерпретатора:

>>> def makedynamo(i):
...     def innerdynamo():
...         print "in dynamo %d" % i
...     innerdynamo.__doc__ = "docstring for dynamo%d" % i
...     innerdynamo.__name__ = "dynamo%d" % i
...     return innerdynamo

>>> dynamo10 = makedynamo(10)
>>> help(dynamo10)
Help on function dynamo10 in module __main__:

dynamo10()
    docstring for dynamo10

Ответ 3

Python позволит вам объявить функцию в функции, поэтому вам не нужно делать трюк exec.

def __init__(self):

    def dynamo(self, arg):
        """ dynamo a dynamic method!
        """
        self.weight += 1
        return arg * self.weight
    self.weight = 50

    setattr(self.__class__, 'dynamo', dynamo)

Если вы хотите иметь несколько версий функции, вы можете поместить все это в цикл и изменить то, что вы называете в функции setattr:

def __init__(self):

    for i in range(0,10):

        def dynamo(self, arg, i=i):
            """ dynamo a dynamic method!
            """
            self.weight += i
            return arg * self.weight

        setattr(self.__class__, 'dynamo_'+i, dynamo)
        self.weight = 50

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

Изменить. Вы можете установить docstring с помощью dynamo.__doc__, чтобы вы могли сделать что-то подобное в своем теле цикла:

dynamo.__doc__ = "Adds %s to the weight" % i

Другое Редактирование. С помощью @eliben и @bobince проблема закрытия должна быть решена.

Ответ 4

Извините меня за плохой английский.

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

во-первых, я создаю список сопоставлений между пунктом меню и фреймом.

menus = [(self.menuItemFile, FileFrame), (self.menuItemEdit, EditFrame)]

первым элементом в отображении является элемент меню, а последний элемент - это кадр, который нужно открыть. Затем я привязываю событие wx.EVT_MENU от каждого пункта меню к определенному кадру.

for menu in menus:
    f = genfunc(self, menu[1])
    self.Bind(wx.EVT_MENU, f, menu[0])

Функция genfunc - это динамический конструктор функций, вот код:

def genfunc(parent, form):
    def OnClick(event):
        f = form(parent)
        f.Maximize()
        f.Show()
    return OnClick