【问题标题】:Python C API - How to assign a value to eval expression?Python C API - 如何为 eval 表达式赋值?
【发布时间】:2019-02-06 13:43:48
【问题描述】:

是否可以在不操作评估字符串的情况下为“评估表达式”赋值?示例:用户编写表达式

"globalPythonArray[10]"

这将评估为 globalPythonArray 的第 10 项的当前值。但目标是,将第 10 项的值设置为新值,而不是获取旧值。一个肮脏的解决方法是,定义一个临时变量“newValue”并将评估字符串扩展到

"globalPythonArray[10] = newValue"

并编译和评估修改后的字符串。是否有一些我可以使用的低级 Python C API 函数,这样我就不必操作评估字符串?

【问题讨论】:

    标签: python c eval python-c-api


    【解决方案1】:

    我会说可能不会,因为访问和存储订阅是不同的操作码:

    >>> dis.dis(compile('globalPythonArray[10]', 'a', 'exec'))
      1           0 LOAD_NAME                0 (globalPythonArray)
                  2 LOAD_CONST               0 (10)
                  4 BINARY_SUBSCR
                  6 POP_TOP
                  8 LOAD_CONST               1 (None)
                 10 RETURN_VALUE
    

    >>> dis.dis(compile('globalPythonArray[10] = myValue', 'a', 'exec'))
      1           0 LOAD_NAME                0 (myValue)
                  2 LOAD_NAME                1 (globalPythonArray)
                  4 LOAD_CONST               0 (10)
                  6 STORE_SUBSCR
                  8 LOAD_CONST               1 (None)
                 10 RETURN_VALUE
    

    另外,在此处插入有关用户输入和eval() 的常规警告:

    globalPythonArray[__import__('os').system('rm -rf /')]
    

    【讨论】:

    • 谢谢!我希望编译后的代码看起来像这样。但是,当我比较两个表达式的 AST 时(例如这里的 vpyast.appspot.com),它们看起来并没有太大的不同,并且可以在将 AST 编译为字节码之前对其进行操作。关于安全性:“用户”(在我看来)实际上是将 Python 嵌入到特定领域的编程语言中的开发人员,他们已经有权直接执行“rm -rf /”。
    【解决方案2】:

    可以通过操作抽象语法树 (AST) 为 eval 表达式“分配”一个值。不需要直接修改评估字符串,如果新值的类型不是太复杂(例如数字或字符串),您可以将其硬编码到 AST 中:

    • 将 eval 表达式编译为 AST。
    • 将根节点处的 Load 表达式上下文替换为 Store
    • 在根节点使用 Assign 语句创建新的 AST。
    • target 设置为修改后的 eval AST 的表达式节点。
    • value 设置为 value。
    • 将新的 AST 编译为字节码并执行。

    示例

    import ast
    import numpy as np
    
    
    def eval_assign_num(expression, value, global_dict, local_dict):
        expr_ast = ast.parse(expression, 'eval', 'eval')
        expr_node = expr_ast.body
        expr_node.ctx = ast.Store()
    
        assign_ast = ast.Module(body=[
            ast.Assign(
                targets=[expr_node],
                value=ast.Num(n=value)
            )
        ])
        ast.fix_missing_locations(assign_ast)
    
        c = compile(assign_ast, 'assign', 'exec')
        exec(c, global_dict, local_dict)
    
    
    class TestClass:
        arr = np.array([1, 2])
        x = 6
    
    
    testClass = TestClass()
    arr = np.array([1, 2])
    
    eval_assign_num('arr[0]', 10, globals(), locals())
    eval_assign_num('testClass.arr[1]', 20, globals(), locals())
    eval_assign_num('testClass.x', 30, globals(), locals())
    eval_assign_num('newVarName', 40, globals(), locals())
    
    print('arr', arr)
    print('testClass.arr', testClass.arr)
    print('testClass.x', testClass.x)
    print('newVarName', newVarName)
    

    输出

    arr [10  2]
    testClass.arr [ 1 20]
    testClass.x 30
    newVarName 40
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-11
      • 2011-09-11
      • 1970-01-01
      相关资源
      最近更新 更多