如何评估有序的dict py文件

moy*_*260 0 python

我有一个名为example_dict.py的文件

#This is a valid comment
{
    'key1': 'value1',
    'key2': 'value2',
    'key3': 'value3',
}
Run Code Online (Sandbox Code Playgroud)

然后我读了这个文件并转换dict:

from collections import OrderedDict
with open("example_dict.py") as fp:
    dict_from_file = OrderedDict( eval( fp.read() ) )
Run Code Online (Sandbox Code Playgroud)

但是这个"dict_from_file"没有相同的顺序key1,key2,key3.

我怎么能以同样的顺序得到这个字典.

Jon*_*nts 6

您可以使用该ast模块编写自定义解析器,作为启动器:

import ast
from collections import OrderedDict

with open('example_dict.py') as fin:
    parsed = ast.parse(fin.read())

first_dict = next(node for node in ast.walk(parsed) if isinstance(node, ast.Dict))
keys = (node.s for node in first_dict.keys)
vals = (node.s for node in first_dict.values)
od = OrderedDict(zip(keys, vals))
# OrderedDict([('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3')])
Run Code Online (Sandbox Code Playgroud)

请注意,尽管这适用于您的示例数据 - 这需要更多的工作才能使其更加健壮,但应该作为起点.

  • 聪明的解决方案!虽然可能有一种方法可以利用更多的`ast`机制来使其更加健壮,通过使用`NodeTransformer`将dict文字转换为元组的'OrderedDict`构造函数.让我看看我是否可以实现这一点......但即使我可以,你的可能性仍然可能更容易理解作为一个起点. (2认同)

aba*_*ert 5

@JonClements的解决方案既美观又简单 - 但是,正如他所指出的那样,它并不那么健壮,因为你依赖于字典显示的每个元素都会评估自己的事实 - 并且你有一些任意的代码其中第一个有效的dict文字是你唯一关心的事情.

一个相关的想法是使用ast.NodeTransformer将dict文字AST转换为OrderedDict构造函数AST,然后就是eval这样.

优点:

  • 一旦您将其用于处理琐碎的案例,它就会自动适用于更复杂的案例.
  • 将它从解析单个字典文字扩展到转换整个模块中的所有字典文字(然后可以作为导入钩子的一部分安装)是很简单的.
  • 您可以了解有关Python AST如何工作的更多信息.

缺点:

  • 还有更多(和更丑陋的)代码要编写,以使其适用于琐碎的案例.
  • 由于您没有手动解析元素,因此添加限制并不是那么容易,例如,安全地处理潜在的恶意或无能的输入(例如,通过literal_eval在每个元素上使用).
  • 您必须了解有关Python AST如何工作的更多信息.

但是,值得回过头来询问您是否真的想要编写并使用所有这些代码.你可能会使用类似开心了很多MacroPy,它能够自动了很多笨重的东西在这里正在做的,和很多我的东西不是在这里做什么(如安装进口钩),让你集中精力的只是一部分你感兴趣的转变.(实际上,我认为MacroPy甚至带有一个odict文字作为其内置示例之一...)


无论如何,变压器看起来像这样:

class DictToOrdered(ast.NodeTransformer):
    def visit_Dict(self, node):
        return ast.fix_missing_locations(ast.copy_location(
            ast.Call(
                func=ast.Attribute(
                    value=ast.Name(id='collections', ctx=ast.Load()),
                    attr='OrderedDict',
                    ctx=ast.Load()),
                args=[ast.Tuple(elts=
                        [ast.Tuple(elts=list(pair), ctx=ast.Load())
                         for pair in zip(node.keys, node.values)],
                        ctx=ast.Load())],
                keywords=[],
                starargs=None,
                kwargs=None),
            node))
Run Code Online (Sandbox Code Playgroud)

这比平时稍微丑陋,因为dict文字不必有上下文(因为它们不能用作赋值目标),但是元组做(因为它们可以),所以我们不能只复制上下文我们做行号的方式.

要使用它:

def parse_dict_as_odict(src):
    import collections
    parsed = ast.parse(src, '<dynamic>', 'eval')
    transformed = DictToOrdered().visit(parsed)
    compiled = compile(transformed, '<dynamic>', 'eval')
    return eval(compiled)
Run Code Online (Sandbox Code Playgroud)

这假设您希望一次只评估一个表达式,并且您希望在当前的全局/本地环境中这样做,并且您不介意将collections模块插入该环境中; 如果你看一下文档的compile,ast.parseeval它应该是明显如何更改其中的任何假设.

所以:

>>> src = '''
... {
...     'key1': 'value1',
...     'key2': 'value2',
...     'key3': 'value3',
... }
... '''
>>> parse_dict_as_odict(src)
OrderedDict([('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3')])
Run Code Online (Sandbox Code Playgroud)

如果你想了解更多信息,而不是自己深入研究源代码,那么Green Tree Snakes是理解Python的AST及其ast模块的一个很好的资源,我希望它是几年前编写的.:)