41

更新:Python 3.7+ 保证 dicts 保留插入顺序

我想使用 .py 文件,如配置文件。因此,使用该{...}符号我可以创建一个使用字符串作为键的字典,但定义顺序在标准 python 字典中丢失了。

我的问题:是否可以覆盖{...}符号以便我得到 aOrderedDict()而不是 a dict()

我希望用 OrderedDict ( dict = OrderedDict) 简单地覆盖 dict 构造函数会起作用,但事实并非如此。

例如:

dict = OrderedDict
dictname = {
   'B key': 'value1',
   'A key': 'value2',
   'C key': 'value3'
   }

print dictname.items()

输出:

[('B key', 'value1'), ('A key', 'value2'), ('C key', 'value3')]
4

7 回答 7

78

这是一个 hack,几乎可以为您提供所需的语法:

class _OrderedDictMaker(object):
    def __getitem__(self, keys):
        if not isinstance(keys, tuple):
            keys = (keys,)
        assert all(isinstance(key, slice) for key in keys)

        return OrderedDict([(k.start, k.stop) for k in keys])

ordereddict = _OrderedDictMaker()
from nastyhacks import ordereddict

menu = ordereddict[
   "about" : "about",
   "login" : "login",
   'signup': "signup"
]

编辑:其他人独立发现了这一点,并odictliteral在 PyPI 上发布了提供稍微更彻底的实现的包 -改用该包

于 2016-05-16T17:38:52.293 回答
40

要从字面上得到你想要的东西,你必须摆弄文件的语法树。我不认为这样做是可取的,但我无法抗拒尝试的诱惑。所以我们开始吧。

首先,我们创建一个具有my_execfile()类似内置函数的模块execfile(),除了所有出现的字典显示,例如{3: 4, "a": 2}由对dict()构造函数的显式调用替换,例如dict([(3, 4), ('a', 2)])。(当然我们可以通过调用来直接替换它们collections.OrderedDict(),但我们不想太打扰。)下面是代码:

import ast

class DictDisplayTransformer(ast.NodeTransformer):
    def visit_Dict(self, node):
        self.generic_visit(node)
        list_node = ast.List(
            [ast.copy_location(ast.Tuple(list(x), ast.Load()), x[0])
             for x in zip(node.keys, node.values)],
            ast.Load())
        name_node = ast.Name("dict", ast.Load())
        new_node = ast.Call(ast.copy_location(name_node, node),
                            [ast.copy_location(list_node, node)],
                            [], None, None)
        return ast.copy_location(new_node, node)

def my_execfile(filename, globals=None, locals=None):
    if globals is None:
        globals = {}
    if locals is None:
        locals = globals
    node = ast.parse(open(filename).read())
    transformed = DictDisplayTransformer().visit(node)
    exec compile(transformed, filename, "exec") in globals, locals

有了这个修改,我们可以通过覆盖来修改字典显示的行为dict。这是一个例子:

# test.py
from collections import OrderedDict
print {3: 4, "a": 2}
dict = OrderedDict
print {3: 4, "a": 2}

现在我们可以使用 运行这个文件my_execfile("test.py"),产生输出

{'a': 2, 3: 4}
OrderedDict([(3, 4), ('a', 2)])

请注意,为简单起见,上述代码不涉及字典推导,应将其转换为传递给dict()构造函数的生成器表达式。您需要向该类添加一个visit_DictComp()方法。DictDisplayTransformer鉴于上面的示例代码,这应该是直截了当的。

同样,我不推荐这种语言语义的混乱。你看过这个ConfigParser模块吗?

于 2011-10-24T18:43:56.373 回答
13

OrderedDict不是“标准python语法”,但是,一组有序的键值对(在标准python语法中)很简单:

[('key1 name', 'value1'), ('key2 name', 'value2'), ('key3 name', 'value3')]

要明确获得OrderedDict

OrderedDict([('key1 name', 'value1'), ('key2 name', 'value2'), ('key3 name', 'value3')])

另一种选择是 sort dictname.items(),如果这就是你所需要的:

sorted(dictname.items())
于 2011-10-24T16:51:04.173 回答
6

从 python 3.6 开始,所有字典都将默认排序。目前,这是一个实现细节,dict不应依赖,但它可能会在 v3.6 之后成为标准。

插入顺序始终保留在新dict实现中:

>>>x = {'a': 1, 'b':2, 'c':3 }
>>>list(x.keys())
['a', 'b', 'c']

从 python 3.6开始, [PEP468]和类属性顺序 [ **kwargsPEP520 ]被保留。新的紧凑有序字典实现用于实现这两者的排序。

于 2016-12-17T04:35:29.467 回答
5

您要求的是不可能的,但是如果JSON语法中的配置文件就足够了,您可以对json模块执行类似的操作:

>>> import json, collections
>>> d = json.JSONDecoder(object_pairs_hook = collections.OrderedDict)
>>> d.decode('{"a":5,"b":6}')
OrderedDict([(u'a', 5), (u'b', 6)])
于 2011-10-24T18:04:58.607 回答
5

我找到的一个解决方案是修补 python 本身,使dict对象记住插入的顺序。

这适用于所有类型的语法:

x = {'a': 1, 'b':2, 'c':3 }
y = dict(a=1, b=2, c=3)

等等

我从https://pypi.python.org/pypi/ruamel.ordereddict/ordereddict获取了C 实现,并合并回了主要的 python 代码。

如果您不介意重新构建 python 解释器,这里是 Python 2.7.8 的补丁: https ://github.com/fwyzard/cpython/compare/2.7.8...ordereddict-2.7.8.diff 。一个

于 2014-12-03T11:30:45.087 回答
0

如果您正在寻找一种获得易于使用的初始化语法的方法 - 考虑创建 OrderedDict 的子类并向其添加更新字典的运算符,例如:

from collections import OrderedDict

class OrderedMap(OrderedDict):
    def __add__(self,other):
        self.update(other)
        return self

d = OrderedMap()+{1:2}+{4:3}+{"key":"value"}

d 将是 - OrderedMap([(1, 2), (4, 3), ('key','value')])


使用切片语法的另一个可能的语法糖示例:

class OrderedMap(OrderedDict):
    def __getitem__(self, index):
        if isinstance(index, slice):
            self[index.start] = index.stop 
            return self
        else:
            return OrderedDict.__getitem__(self, index)

d = OrderedMap()[1:2][6:4][4:7]["a":"H"]
于 2014-09-17T11:25:27.747 回答