如何在类中注入代码并在运行时为类字段赋值?
Posted
技术标签:
【中文标题】如何在类中注入代码并在运行时为类字段赋值?【英文标题】:How to inject code in class and assign values to class fields at runtime? 【发布时间】:2019-07-24 08:56:02 【问题描述】:我有一个 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>
我正在使用 https://github.com/etingof/apacheconfig 将此文件转换为 python dict
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 字典中提供这些可调用对象。
【问题讨论】:
看起来很简单的string.replace('%...%', name)
@stovfl 是的,替换部分很简单,但可调用(上例中的函数 foo 和 bar)不能直接在 BatchJobSpec 类字典中使用。可调用对象在“pythoncode”字段中以格式化字符串的形式出现。
【参考方案1】:
评论:我有 foo 和 bar。 exec(str) 将同时执行
在您的self.pythoncode
中,您定义了def foo():
等函数。因此exec
不会执行,而是在本地命名空间中define这些函数。要将这些函数作为class methode
执行,您必须在class
本身中创建一个引用这些本地函数的attribute
。
在这个例子中,函数名是事先知道的
class BatchJobSpec:
def __init__(self, pythoncode , batch):
self.pythoncode = pythoncode
self.name = batch['name']
self._exec = ['for', 'bar']
self.exec()
要使self
在本地命名空间中可见,请定义locals_
字典。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])
用法:不同的pythonformat
语法,因为我用的是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()))
输出:
__setattr__(foo) __setattr__(bar) echo foo: I am p&l_calculator echo bar: I am p&l_calculator
library/functions -问题我希望在尝试访问“batchjobspec.exec”的值时对其进行插值
exec
object.__setattr__
将您的 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
【讨论】:
感谢您的回答,但请注意课程中不存在“foo”和“bar”函数。它们在“self.pythoncode”字段内,我怎样才能将这些函数注入到类中?我是否需要为 pythoncode 字段添加 setter 并从那里更新类字典? foo 已经存在于 self.pythoncode 中,我不想创建任何新的 foo。 @LokeshAgrawal: 否,在.pythoncode
中是str
,创建的self.foo
是通过exec(<code>)
执行的对该字符串的引用
exec 将执行 str 中的所有代码。 str 中可以有很多函数。作为一个例子检查我的问题,我有 foo 和 bar。 exec(str) 将同时执行。
能否请您概括一下这个解决方案,不要假设只有 foo 和 bar 函数。以上是关于如何在类中注入代码并在运行时为类字段赋值?的主要内容,如果未能解决你的问题,请参考以下文章