exec не работает при создании функций из строки - PullRequest
0 голосов
/ 26 марта 2019

Короче говоря, я тестирую динамическое внедрение свойств в методах на python.

сейчас у меня проблема в том, что когда я вызываю exec () для моих строк getter и setter, чтобы превратить их в динамически создаваемую функцию, они остаются строками.

def _injectProperties(self): 
        """docstring"""

        for p in self.InputParameters:
            index = p.Order
            name = p.Name.lstrip('@')
            pName = p.Name

            fnGet = (
                "def get(self): \n"
                "    rtnVal = [p.Value for p in self.InputParameters "
                "        if p.Name == '{0}'][0] \n"
                "    return rtnVal ").format(p.Name)

            fnSet = (
                "def set(self, value): \n"
                "    prop = [p for p in self.InputParameters "
                "        if p.Name == '{0}'][0] \n"
                "    prop.Value = value \n"
                "    return ").format(p.Name)

            exec(fnGet) in locals()
            exec(fnSet) in locals()

            self._addprop(name, fnGet, fnSet)

        return

Так что в основном ввышеприведенный код _addprop - это функция, которая просто создает копию класса и устанавливает для него свойство следующим образом:

setattr(cls, name, property(fget=getter, fset=setter, fdel=destructor, doc=docstring))

, почему в этом контексте переменные fnGet и fnSet все еще сохраняют переменныессылаться на строковое представление функции get и set после того, как я вызову exec(fnGet) и exec(fnSet)?

Ответы [ 2 ]

1 голос
/ 26 марта 2019

Вы не указали MCVE в своем вопросе. поэтому я сделал что-то работоспособное, чтобы проиллюстрировать, как вы можете это сделать (хотя я думаю, что у @Ned Batchelder, вероятно, есть лучшее предложение).

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

from textwrap import dedent

class InputParameter:  # Mock for testing
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)


class Class:
    def __init__(self):
        self.InputParameters = [  # For testing
            InputParameter(Order=42, Name='@foobar'),
        ]

    def _addprop(self, name, getter, setter):
        print('_addprop({!r}, {}, {})'.format(name, getter, setter))

    def _injectProperties(self):
            """docstring"""

            for p in self.InputParameters:
                index = p.Order
                name = p.Name.lstrip('@')
                pName = p.Name

                fnGet = dedent("""
                    def get(self):
                        rtnVal = [p.Value for p in self.InputParameters
                                    if p.Name == '{0}'][0]
                        return rtnVal
                """).format(p.Name)

                fnSet = dedent("""
                    def set(self, value):
                        prop = [p for p in self.InputParameters
                                    if p.Name == '{0}'][0]
                        prop.Value = value
                        return
                """).format(p.Name)

                locals_dict = {}
                exec(fnGet, globals(), locals_dict)
                exec(fnSet, globals(), locals_dict)

                self._addprop(name, locals_dict['get'], locals_dict['set'])

            return

cls = Class()
cls._injectProperties()

Выход:

_addprop('foobar', <function get at 0x00270858>, <function set at 0x00572C00>)
1 голос
/ 26 марта 2019

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

Я думаю, это делает то, что вам нужно:

def __getattr__(self, attr):
    for p in self.InputParameters:
        if p.Name == attr:
            return p.Value

def __setattr__(self, attr, value):
    for p in self.InputParameters:
        if p.Name == attr:
            p.Value = value
            break
...