如何在literal_eval之前用Python AST中的Dict替换OrderedDict?

Jim*_*ker 6 python abstract-syntax-tree

我有一个包含Python代码的字符串,我可以将其评估为Python,literal_eval如果它只有OrderedDict替换的实例{}.

我正在尝试使用ast.parseast.NodeTransformer进行替换,但是当我捕获节点时nodetype == 'Name' and node.id == 'OrderedDict',我找不到节点对象中的参数列表,以便我可以用Dict节点替换它.

这甚至是正确的方法吗?

一些代码:

from ast import NodeTransformer, parse

py_str = "[OrderedDict([('a', 1)])]"

class Transformer(NodeTransformer):
    def generic_visit(self, node):
        nodetype = type(node).__name__

        if nodetype == 'Name' and node.id == 'OrderedDict':
            pass # ???

        return NodeTransformer.generic_visit(self, node)

t = Transformer()

tree = parse(py_str)

t.visit(tree)
Run Code Online (Sandbox Code Playgroud)

Leo*_*o K 2

这个想法是将所有OrderedDict表示为ast.Call具有特定属性的节点(可以从下面看到ordered_dict_conditions)替换为从参数中提取/参数的ast.Dict节点。keyvalueast.Call

import ast


class Transformer(ast.NodeTransformer):
    def generic_visit(self, node):
        # Need to call super() in any case to visit child nodes of the current one.
        super().generic_visit(node)
        ordered_dict_conditions = (
            isinstance(node, ast.Call)
            and isinstance(node.func, ast.Name)
            and node.func.id == 'OrderedDict'
            and len(node.args) == 1
            and isinstance(node.args[0], ast.List)
        )
        if ordered_dict_conditions:
            return ast.Dict(
                [x.elts[0] for x in node.args[0].elts],
                [x.elts[1] for x in node.args[0].elts]
            )
        return node


def transform_eval(py_str):
    return ast.literal_eval(Transformer().visit(ast.parse(py_str, mode='eval')).body)


print(transform_eval("[OrderedDict([('a', 1)]), {'k': 'v'}]"))  # [{'a': 1}, {'k': 'v'}]
print(transform_eval("OrderedDict([('a', OrderedDict([('b', 1)]))])"))  # {'a': {'b': 1}}
Run Code Online (Sandbox Code Playgroud)

笔记

因为我们想首先替换最里面的节点,所以我们super()在函数的开头调用 。

每当OrderedDict遇到节点时,都会使用以下内容:

  • node.args是一个包含调用参数的列表OrderedDict(...)
  • 此调用有一个参数,即包含键值对作为元组的列表,可以通过node.args[0]( ast.List) 访问,并且node.args[0].elts是包装在list.
  • node.args[0].elts[i]不同的ast.Tuples ( ) 的元素也for i in range(len(node.args[0].elts))可以通过属性再次访问.elts
  • 最后node.args[0].elts[i].elts[0]是调用中使用的键和node.args[0].elts[i].elts[1]OrderedDict

然后,后面的键和值用于创建一个新ast.Dict实例,然后用该实例替换当前节点(原为ast.Call)。

  • 已更新以允许嵌套结构。作为处理“增强”文字安全解析的一种可能方法,这变得很有趣。呼吁@a_guest 和其他有经验的人编辑或发布改进建议 - 并将其转变为 Wiki 主题。 (2认同)