#python #python-3.x #metaclass #python-internals
#python #python-3.x #метакласс #python-внутренности
Вопрос:
У меня есть файл спецификации 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 pamp;l_calculator
exec echo %foo()%
</batch>
Я конвертирую этот файл в python dict с помощью 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': 'pamp;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}"
# pamp;l_calculator
# echo %foo()%
Проблема: я хочу, чтобы значение «batchjobspec.exec» интерполировалось при попытке доступа к нему, т. Е. оно не должно быть «echo %foo()%», а должно быть «echo foo: I am p amp; l_calculator».
т. е. каким-то образом в получателе полей я хочу проверить, есть ли синтаксис «% %». Если да, и если содержимое внутри «% %» содержит вызываемый объект, то я хочу вызвать этот вызываемый объект и добавить значение, возвращаемое из callable, к значению соответствующего поля. Я предполагаю, что мне также придется сделать эти вызываемые объекты доступными в BatchJobSpec dict .
Комментарии:
1. Выглядит как простой
string.replace('%...%', name)
2. @stovfl да, заменяющая часть проста, но вызываемая (функция foo и bar в примере выше) не будет доступна непосредственно в классе BatchJobSpec dict. Вызываемые объекты представлены в виде форматированной строки внутри поля «pythoncode».
Ответ №1:
Комментарий: У меня есть foo, а также bar. exec(str) выполнит оба
В вашем self.pythoncode
у вас есть определения таких функций, как def foo():
. Поэтому exec
не выполняется, но определяет эти функции в локальном пространстве имен. Чтобы выполнить эти функции как a class methode
, вы должны создать attribute
, ссылающийся на эти локальные функции, в class
самом.
-
В этом примере имена функций известны заранее
class BatchJobSpec: def __init__(self, pythoncode , batch): self.pythoncode = pythoncode self.name = batch['name'] self._exec = ['for', 'bar'] self.exec()
-
Чтобы сделать
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])
-
Использование: Другой синтаксис python
format
, поскольку я использую python 3.5python = """ 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()))
-
Вывод:
__setattr__(foo) __setattr__(bar) echo foo: I am pamp;l_calculator echo bar: I am pamp;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
Комментарии:
1. Спасибо за ответ, но, пожалуйста, обратите внимание, что функции «foo» и «bar» отсутствуют в классе. Они находятся внутри поля «self.pythoncode», как я могу внедрить эти функции в класс? Нужно ли мне добавить параметр setter для поля pythoncode и обновить class dict оттуда?
2. foo уже есть в self.pythoncode, я не хочу создавать никакого нового foo.
3. @LokeshAgrawal: Нет , в
.pythoncode
естьstr
, созданноеself.foo
является ссылкой на эту строку, выполняемую черезexec(<code>)
4. exec выполнит весь код в str. В str может быть много функций. В качестве примера проверьте в моем вопросе, что у меня есть foo, а также bar. exec(str) выполнит оба.
5. Не могли бы вы, пожалуйста, обобщить это решение, не предполагая, что будут только функции foo и bar.