Как внедрить код в класс и присвоить значения полям класса во время выполнения? - PullRequest
0 голосов
/ 02 марта 2019

У меня есть файл спецификации BatchJob (batch.spec), похожий на приведенный ниже:

python = <<EOF

def foo():
    return f"foo: I am {self.name}"

def bar():
    return f"bar: I am {self.name}"

EOF

<batch>
  name p&l_calculator
  exec echo %foo()%
</batch>

Я преобразую этот файл в файл Python, используя https://github.com/etingof/apacheconfig

from apacheconfig import *

with make_loader() as loader:
    config = loader.load('batch.spec')

# Print content of python dict
for key, value in config.items():
    print(key, value)

# output from print statement above
# python 
# def foo():
#     return f"foo: I am {self.name}"
#
# def bar():
#     return f"bar: I am {self.name}"
# batch {'name': 'p&l_calculator', 'exec': 'echo %foo()%'}

Теперь я преобразовываю этот python dict в объект BatchJobSpec, как показано ниже:

class BatchJobSpec:

    def __init__(self, pythoncode , batch):
        self.pythoncode = pythoncode
        self.name = batch.get("name")
        self.exec = batch.get("exec")

batchjobspec = BatchJobSpec(config.get("python"), config.get("batch"))

Если я напечатаю поля batchjobspec, то получу что-то вроде ниже

print(batchjobspec.pythoncode)
print(batchjobspec.name)
print(batchjobspec.exec) 

# Output from above print statements
# def foo():
#     return f"foo: I am {self.name}"
#
# def bar():
#     return f"bar: I am {self.name}"
# p&l_calculator
# echo %foo()%

Проблема: Я хочу, чтобы значение "batchjobspec.exec" интерполировалось при попытке получить к нему доступ, т.е. оно не должно быть "echo% foo ()%", а должно быть "echo foo: я p & l_calculator».

т.е. как-то в получателе полей, я хочу проверить, есть ли синтаксис "%%".Если да, и если содержимое внутри "%%" содержит вызываемый объект, то я хочу вызвать этот вызываемый элемент и добавить значение, возвращаемое из вызываемого элемента, к значению соответствующего поля.Полагаю, мне придется сделать эти вызовы доступными и в BatchJobSpec.

1 Ответ

0 голосов
/ 02 марта 2019

Комментарий : У меня есть foo и bar.exec (str) выполнит оба

В вашем self.pythoncode есть определенные функции, такие как def foo():.Следовательно, exec не выполняет , а определяет эти функции в локальном пространстве имен.Чтобы выполнить эти функции как class methode, вам нужно создать attribute, ссылающийся на эти локальные функции, в самом class.

  1. Вв этом примере имена функций известны заранее

    class BatchJobSpec:
        def __init__(self, pythoncode , batch):
            self.pythoncode = pythoncode
            self.name = batch['name']
            self._exec = ['for', 'bar']
            self.exec()
    
  2. Чтобы сделать self видимым в локальном пространстве имен, определите locals_ dict.
    exec строкуself.pythoncode приводит к вставке ссылки на функции в locals_.Чтобы использовать эти локальные ссылки как class methode и сделать его постоянным, используйте self.__setattr__(....

        def exec(self):
            locals_ = {'self': self}
            exec(self.pythoncode, locals_)
            for attrib in self._exec:
                print('__setattr__({})'.format(attrib))
                self.__setattr__(attrib, locals_[attrib])
    
  3. Использование : другой синтаксис python format, как я использую Python 3,5

    python = """
    def foo():
        return 'foo: I am {name}'.format(name=self.name)
    
    def bar():
        return 'bar: I am {name}'.format(name=self.name)
    """
    
    if __name__ == "__main__":
        batchjobspec = BatchJobSpec(config.get("python"), config.get("batch"))
        print('echo {}'.format(batchjobspec.foo()))
        print('echo {}'.format(batchjobspec.bar()))
    
  4. Выход :

    __setattr__(foo)
    __setattr__(bar)
    echo foo: I am p&l_calculator
    echo bar: I am p&l_calculator
    

Вопрос Я хочу, чтобы значение "batchjobspec.exec" интерполировалось при попытке получить к нему доступ


Измените class BatchJobSpec на:

class BatchJobSpec:

    def __init__(self, pythoncode , batch):
        self.pythoncode = pythoncode
        self.name = batch.get("name")
        self._exec = batch.get("exec")

    @property
    def exec(self):
        to_exec = 'foo'
        self.__setattr__(to_exec, lambda : exec('print("Hello world")'))

        self.foo()
        return None
...