您如何仅在对象上下文中执行 python 'eval'? [英] How do you do a python 'eval' only within an object context?

查看:33
本文介绍了您如何仅在对象上下文中执行 python 'eval'?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以做类似的事情

c = MyObj()c.eval("func1(42)+func2(24)")

在 Python..i.e.func1() 和 func2() 是否在对象c"的范围内进行评估(如果它们是该类定义中的成员函数)?我无法进行简单的解析,因为对于我的应用程序,eval 字符串可以变得任意复杂.我想用 ast 模块做一些魔术可能会成功,但由于关于 ast 的文献很脏,我不知道去哪里看:

导入 ast类 MyTransformer(ast.NodeTransformer):defvisit_Name(self, node):# 做一个 generic_visit 以便处理子节点ast.NodeVisitor.generic_visit(自我,节点)返回 ast.copy_location(# 用函数名做一些神奇的事情,使它们变成# 对公式对象的方法调用新节点,节点)类公式(对象):定义左(自我,s,n):返回 s[:n]def RIGHT(self, s, n):返回 s[0-n:]def CONCAT(self, *args, **kwargs):return ''.join([args in args])定义主():evalString = "CONCAT(LEFT('Hello', 2), RIGHT('World', 3))"# 我们想要模拟一些类似 Formula().eval(evalString) 的东西node = ast.parse(evalString, mode='eval')MyTransformer().visit(node)ast.fix_missing_locations(节点)打印 eval(compile(node, '<string>', mode='eval'))

解决方案

您几乎肯定不想这样做,但您可以.

eval 的上下文是您想在其中评估代码的 globals 和 locals 字典.最常见的情况可能是 eval(expr, globals(), mycontext)eval(expr, mycontext),它们分别替换了默认的本地和全局上下文,让另一个单独存在.用对象的字典替换本地上下文类似于运行该对象的内部"(方法)——尽管请记住,如果不这样做,作为成员函数"并不会像您预期的那样好有一个 self 来调用其他成员函数......

无论如何,这是一个简单的例子:

<预><代码>>>>类 Foo(对象):... def __init__(self):... self.bar = 3>>>foo = foo()>>>eval('bar', globals(), foo.__dict__)3

请记住,__dict__ 可能不是您想要的.例如:

<预><代码>>>>类 Foo(对象):... @staticmethod...定义栏():...返回 3>>>foo = foo()>>>eval('bar()', globals(), foo.__dict__)NameError: 名称 'bar' 未定义>>>eval('bar()', globals(), {k: getattr(foo, k) for k in dir(foo)}3

要使这项工作以您想要的方式工作,您必须确切地知道如何用 Python 术语定义您想要的——这需要了解一些对象在幕后如何工作(MRO,可能是描述符等).

如果您确实需要 eval,并且您确实需要提供任意上下文,那么您最好显式构建这些上下文(作为字典),而不是试图强制对象进入该角色:<预><代码>>>>富 = {...酒吧":拉姆达:3... }>>>eval('bar()', globals(), foo)

无论如何,这种用法更接近于您尝试在 Python 中模拟的 Javascript 样式.

当然,与 JS 不同,Python 不允许您将多行定义放在表达式中,因此对于复杂的情况,您必须这样做:

<预><代码>>>>定义栏():...返回 3>>>富 = {...'酒吧':酒吧... }>>>eval('bar()', globals(), foo)

但可以说这几乎总是更具可读性(这基本上是 Python 不允许表达式中的多行定义背后的论点).

Is it possible to do something like

c = MyObj()
c.eval("func1(42)+func2(24)")

in Python..i.e. have func1() and func2() be evaluated within the scope of the object 'c' (if they were member functions within that class definition)? I can't do a simple parsing, since for my application the eval strings can become arbitrarily complicated. I guess doing some magic with the ast module might do the trick, but due to the dirth of literature on ast, I'm not sure where to look:

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'))    

解决方案

You almost certainly don't want to do this, but you can.

The context for eval is the globals and locals dictionaries that you want to evaluate your code in. The most common cases are probably eval(expr, globals(), mycontext) and eval(expr, mycontext), which replace the default local and global contexts, respectively, leaving the other alone. Replacing the local context with an object's dictionary is similar to running "within" (a method of) that object—although keep in mind that "being a member function" doesn't do as much good as you might expect if you don't have a self to call other member functions on…

Anyway, here's a quick example:

>>> class Foo(object):
...     def __init__(self):
...         self.bar = 3
>>> foo = Foo()
>>> eval('bar', globals(), foo.__dict__)
3

Keep in mind that __dict__ may not be exactly what you want here. For example:

>>> 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

To make this work the way you want, you have to know exactly how to define you want, in Python terms—which requires knowing a bit about how objects works under the covers (MRO, maybe descriptors, etc.).

If you really need eval, and you really need to provide arbitrary contexts, you're probably better building those contexts explicitly (as dictionaries) rather than trying to force objects into that role:

>>> foo = {
...     'bar': lambda: 3
... }
>>> eval('bar()', globals(), foo)

This use is much closer to the Javascript style you're trying to emulate in Python anyway.

Of course, unlike JS, Python doesn't let you put multi-line definitions inside an expression, so for complex cases you have to do this:

>>> def bar():
...     return 3
>>> foo = {
...     'bar': bar
... }
>>> eval('bar()', globals(), foo)

But arguably that's almost always more readable (which is basically the argument behind Python not allowing multi-line definitions in expressions).

这篇关于您如何仅在对象上下文中执行 python 'eval'?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆