我有一个名为example_dict.py的文件
#This is a valid comment
{
'key1': 'value1',
'key2': 'value2',
'key3': 'value3',
}
然后我读了这个文件并转换dict:
from collections import OrderedDict
with open("example_dict.py") as fp:
dict_from_file = OrderedDict( eval( fp.read() ) )
但是这个" dict_from_file"没有相同的顺序key1,key2,key3。
我怎么能以同样的顺序得到这个字典。
答案 0 :(得分: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')])
请注意,尽管这适用于您的示例数据 - 这需要更多工作才能使其更加健壮,但应该作为起点。
答案 1 :(得分:5)
@ JonClements'解决方案既美观又简单 - 但是,正如他所指出的那样,它并不那么强大,因为你依赖于字典显示的每个元素都会对自身进行评估这一事实 - 而且你需要这样做。我得到了一些任意代码,其中第一个有效的字典文字是你唯一关心的。
一个相关的想法是使用ast.NodeTransformer
将dict文字AST转换为OrderedDict构造函数AST,然后只用eval
。
优点:
缺点:
literal_eval
元素)。但是,值得回过头来询问您是否真的想要编写并使用所有这些代码。使用像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))
这比平时稍微丑陋,因为字面文字不必具有上下文(因为它们不能用作赋值目标),但是元组(因为它们可以),所以我们不能像我们行号那样复制上下文。
使用它:
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)
假设您希望一次只评估一个表达式,并且您希望在当前的全局/本地环境中这样做,并且您不介意将collections
模块插入到该表达式中环境;如果您查看compile
,ast.parse
和eval
的文档,那么应该明白如何更改任何这些假设。
所以:
>>> src = '''
... {
... 'key1': 'value1',
... 'key2': 'value2',
... 'key3': 'value3',
... }
... '''
>>> parse_dict_as_odict(src)
OrderedDict([('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3')])
如果您想了解更多信息,而不是自己深入研究源代码,Green Tree Snakes是理解Python的AST及其ast
模块的理想资源。几年前。 :)
答案 2 :(得分:1)
Python词典没有任何固有的顺序。您可能已经知道这一点,因为您正在尝试将数据放入OrderedDict
的实例中,这样可以保持其值的添加顺序。
但是,您遇到的问题是您的eval
表达式首先生成普通的dict
实例,并且只有在订单丢失后才会传递给{ {1}}。
没有直接解决这个问题。如果您使用OrderedDict
来解析其中包含字典文字的文件,那么它将为您提供常规eval
。
但还有其他选择。您可以编写自己的解析代码,并创建值以直接放入dict
,而无需先创建常规OrderedDict
。这有点复杂,如果这是你要采用的方法,你应该选择更好的文件格式。
如果实际上您可以更改文件的内容,您可以简单地让dict
调用创建一些其他数据结构,您可以传递给eval
而不会丢失订购信息。 OrderedDict
2元组列表是一个不错的选择,不需要对代码进行其他更改:
(key,value)
请注意,在Python的某个未来版本中,函数调用中传递的关键字参数可能会被放入[
('key1', 'value1'),
('key2', 'value2'),
('key3', 'value3'),
]
而不是OrderedDict
(如PEP 468中所述)。如果发生这种情况,您可以将文件内容更改为以下内容,并直接从dict
获取OrderedDict
:
eval
唉,如果您今天尝试这个,那么您将遇到当前代码所遇到的相同问题(关键字参数被打包到常规OrderedDict(
key1='value1',
key2='value2',
key3='value3',
)
中,这会在dict
代码之前丢弃它们的排序看看他们)。 OrderedDict
构造函数的关键字参数并不十分有用。