如何在运行时将代码注入类并为类字段分配值?

时间:2019-03-02 14:38:10

标签: python python-3.x metaclass python-internals

我有一个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字典转换为如下所示的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”。

即在字段的getter中,我想检查是否存在“%%”语法。如果是,并且“%%”中的内容包含可调用对象,那么我想调用该可调用对象,并将从可调用对象返回的值附加到相应字段的值中。我想我也必须在BatchJobSpec字典中提供这些可调用对象。

1 个答案:

答案 0 :(得分:1)

  

评论:我既有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_字典。
    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