我有一个包含Python代码的字符串,如果它只用literal_eval
替换了OrderedDict
的实例,那么我可以将{}
视为Python。
我正在尝试使用ast.parse
和ast.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)
答案 0 :(得分:2)
该想法是将所有具有特定属性(可以从下面的OrderedDict
看到的ast.Call
表示为ordered_dict_conditions
的节点替换为{{1} 1}} / ast.Dict
参数是从key
参数中提取的。
value
由于我们要首先替换最里面的节点,因此在函数的开头放置了对ast.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}}
节点,就会使用以下内容:
super()
是一个包含OrderedDict
调用参数的列表。node.args
(OrderedDict(...)
)可以访问,而node.args[0]
是包装在{中的元组{1}}。ast.List
是不同的node.args[0].elts
(list
),其元素可以通过node.args[0].elts[i]
属性再次访问。ast.Tuple
是键,for i in range(len(node.args[0].elts))
是在.elts
调用中使用的值。随后的键和值将用于创建一个新的node.args[0].elts[i].elts[0]
实例,然后将其用于替换当前节点(即node.args[0].elts[i].elts[1]
)。
答案 1 :(得分:0)
您可以使用ast.NodeVisitor
类来观察OrderedDict
树,以便从遇到的节点中手动构建{}
树,并使用从空dict解析的节点作为基础
import ast
from collections import deque
class Builder(ast.NodeVisitor):
def __init__(self):
super().__init__()
self._tree = ast.parse('[{}]')
self._list_node = self._tree.body[0].value
self._dict_node = self._list_node.elts[0]
self._new_item = False
def visit_Tuple(self, node):
self._new_item = True
self.generic_visit(node)
def visit_Str(self, node):
if self._new_item:
self._dict_node.keys.append(node)
self.generic_visit(node)
def visit_Num(self, node):
if self._new_item:
self._dict_node.values.append(node)
self._new_item = False
self.generic_visit(node)
def literal_eval(self):
return ast.literal_eval(self._list_node)
builder = Builder()
builder.visit(ast.parse("[OrderedDict([('a', 1)])]"))
print(builder.literal_eval())
请注意,这仅适用于示例的简单结构,该结构使用str
作为键,而int
作为值。但是,应该以类似的方式扩展到更复杂的结构。
答案 2 :(得分:0)
除了使用ast
来解析和转换表达式之外,您还可以使用正则表达式来执行此操作。例如:
>>> re.sub(
... r"OrderedDict\(\[((\(('[a-z]+'), (\d+)\)),?\s*)+\]\)",
... r'{\3: \4}',
... "[OrderedDict([('a', 1)])]"
... )
"[{'a': 1}]"
以上表达式基于OP的示例字符串,并将单引号字符串作为键,将正整数作为值,但是当然可以将其扩展到更复杂的情况。