是否有可能做这样的事情
c = MyObj()
c.eval("func1(42)+func2(24)")
在Python..ie具有func1的()和FUNC2()对象“C”的范围之内进行评估(如果他们是该类定义中的成员函数)? 我不能做一个简单的分析,因为我的应用程序中的eval字符串可以成为任意复杂。 我猜做一些魔法与AST模块可能做的伎俩,但由于对AST文学dirth,我不知道去哪里找:
import ast
class MyTransformer(ast.NodeTransformer):
def visit_Name(self, node):
# do a generic_visit so that child nodes are processed
ast.NodeVisitor.generic_visit(self, node)
return ast.copy_location(
# do something magical with names that are functions, so that they become
# method calls to a Formula object
newnode,
node
)
class Formula(object):
def LEFT(self, s, n):
return s[:n]
def RIGHT(self, s, n):
return s[0-n:]
def CONCAT(self, *args, **kwargs):
return ''.join([arg for arg in args])
def main():
evalString = "CONCAT(LEFT('Hello', 2), RIGHT('World', 3))"
# we want to emulate something like Formula().eval(evalString)
node = ast.parse(evalString, mode='eval')
MyTransformer().visit(node)
ast.fix_missing_locations(node)
print eval(compile(node, '<string>', mode='eval'))
你几乎肯定不希望这样做,但你可以 。
对于上下文eval
是要评估在你的代码中的全局和当地人词典,最常见的情况是可能eval(expr, globals(), mycontext)
和eval(expr, mycontext)
取代默认的本地和全球上下文,分别留下其他孤单。 与对象的字典更换当地的情况是类似“内”运行(的方法)对象 - 虽然牢记,“作为一个成员函数”并不做尽可能多好,如果你没有,你可能期望有一个self
来调用其他成员函数...
总之,这里是一个简单的例子:
>>> class Foo(object):
... def __init__(self):
... self.bar = 3
>>> foo = Foo()
>>> eval('a', globals(), foo.__dict__)
3
请记住, __dict__
可能不是你想在这里什么。 例如:
>>> class Foo(object):
... @staticmethod
... def bar():
... return 3
>>> foo = Foo()
>>> eval('bar()', globals(), foo.__dict__)
NameError: name 'bar' is not defined
>>> eval('bar()', globals(), {k: getattr(foo, k) for k in dir(foo)}
3
为了使这项工作您想要的方式,你必须知道如何定义你想要的,在Python方面,它需要知道一点关于如何盖(MRO,也许描述符等)下的对象的作品。
如果你真的需要eval
,你真的需要提供任意环境中,你可能会更好构建这些上下文明确(如字典),而不是试图迫使物体进入那个角色:
>>> foo = {
... 'bar': lambda: 3
... }
>>> eval('bar()', globals(), foo)
这种使用是更接近你想在Python效仿反正JavaScript的风格。
当然,不同的JS,Python没有让你把多行定义一个表达式中,因此对于复杂的情况下,你必须这样做:
>>> def bar():
... return 3
>>> foo = {
... 'bar': bar
... }
>>> eval('bar()', globals(), foo)
不过,事实上这几乎总是更可读的(这基本上是落后的Python不允许在表达式多行定义的参数)。
所以,我建议你做这样的事情:
>>> class S(object):
... def add(self, a, b):
... return a + b
...
>>> filter(lambda (k,v): not k.startswith("__"), S.__dict__.items())
[('add', <function add at 0x109cec500>)]
>>> target = S()
>>> map(lambda (k, f): (k, f.__get__(target, S)), filter(lambda (k,v): not k.startswith("__"), S.__dict__.items()))
[('add', <bound method S.add of <__main__.S object at 0x109ce4ed0>>)]
>>> dict(_)
{'add': <bound method S.add of <__main__.S object at 0x109ce4ed0>>}
>>> eval("add(45, 10) + add(10, 1)", _, {})
66
好像你需要的。 让我来解释一下这是如何工作的。
-
eval
接受当地人和全局作为参数。 - 因此,我们需要定义特殊的全球背景下,这将是你的类的“代表”。
- 要做到这一点,我们需要提供作为
globals
的所有“有价值的” 有限方法字典。 - 从simples部分开始。 我们有
S
类定义。 如何让所有的“宝贵”的方法呢? 简单的filter
,从名字S.__dict__
为了检查方法名称是否从开始__
或没有(你看,作为结果,我们得到名单有1项- add
功能)。 - 创建
target
的=实例S
类,这将是“EVAL语境”。 - 下一步是最“靠谱”。 我们需要从每个函数创建“绑定的方法”。 要做到这一点,我们使用这些事实,该类
__dict__
商店功能,每个功能的非数据描述符和界方法可以简单地被撷取func.__get__(obj, type(obj))
在执行此操作map
。 - 就拿上一步骤产生,创建
dict
从它。 - 通过为
globals
到eval
功能。
我希望这个能帮上忙。
以上建议填充液locals
适用于大多数情况下,但在性质(数据描述)的情况下,可能会有问题。 填充字典时,这些被评估一次 。 这意味着,相同的变量名多次引用总是会返回完全相同的情况下,这可能不是预期中的属性时的行为。
这个问题可以通过注意被解决eval
期望一个locals
的论点,即表现得像dict
(相对于全局变量,它必须是一个dict
)。 换句话说,我们可以重写__getitem__
在您的实例上的实例的上下文飞解决变量名称,并直接把它作为locals
归因于eval
。 你的例子因此可以实现:
class Formula(object):
def __getitem__(self, key):
if key not in dir(self) or key.startswith('__'):
raise KeyError(key)
return getattr(self, key)
def LEFT(self, s, n):
return s[:n]
def RIGHT(self, s, n):
return s[0-n:]
def CONCAT(self, *args, **kwargs):
return ''.join([arg for arg in args])
def main():
evalString = "CONCAT(LEFT('Hello', 2), RIGHT('World', 3))"
print eval(evalString, {}, Formula())
if __name__ == "__main__":
main()
这一招应该继承,静态方法,类方法和属性的作用。 最后,使用dir
和getattr
避免了需要直接与互动__dict__
或__mro__
,虽然结果dir
可能并不总是完整 。
你可能有一个看接受这个问题的答案: “获得是在与语句要执行的命令块” 。
这是一个有用的方式为我创造我自己的背景,其中在矩形阵列,如Python熊猫数据帧的数学运算,“只是工作”,而无需使用额外的丑熊猫语法来打扰。 例如,当我写“ a = x*y
”的上下文之内,它自动分配a
作为对上下文对象中的属性,并且它知道与上下文对象的执行矢量操作x
和y
属性。
,我发现这方面的东西是非常非常有帮助的,尽管事实上,每当我问StackOverflow上,我经常收到trollish响应,它不能是我真正想做的事情。
你也许可以得到这个在该环境中工作eval
查找功能了。