【问题标题】:Python order Dict with a pre-defined orderPython order 具有预定义顺序的字典
【发布时间】:2019-04-03 17:29:45
【问题描述】:

接收一个字典并返回一个新的有序字典

从 api 返回的 json:

{
    'host': '192.168.0.1',
    'name': 'my_new_name',
    'port': 443,
    'something_more': 'hello'
}

目标:

{
    'name': 'my_new_name',
    'something_more': 'hello',
    'host': '192.168.0.1',
    'port': 443
}

有什么方法可以定义键的顺序吗?

例如在goal上实现这个顺序:

key_order = ('name', 'something_more', 'host', 'port')

谢谢

【问题讨论】:

  • 您使用的是什么版本的 Python? 3.7 具有用户可定义的 dict 顺序,但如果更早,您可能会不走运。
  • 你试过什么?你知道枚举方法吗?您可以使用 key_order 元组上的 enumerate 方法来获取每个索引处的索引和元素,以从 API 返回的 json 中获取值。
  • 目标是以这种方式打印字典还是实际存储它?基本上你想用订单完成什么?
  • @Kevin 我正在使用 python 3.6.3
  • @user1234 你只是重申了你已经说过的话,没有额外的信息。

标签: python json dictionary ordereddictionary


【解决方案1】:

如果您使用的是 Python 3.7 或更高版本*,您可以在 dict 中指定项目的顺序,因为插入顺序会保留。创建一个新字典并以您喜欢的任何顺序插入项目:

def reorder_items(d, keys):
    d = d.copy() #we're going to destructively modify d, so make a copy first
    result = {}
    for key in keys:
        if key in d:
            result[key] = d.pop(key)
    #the user might not have supplied all the keys belonging to d, 
    #so insert anything we haven't touched yet
    result.update(d)
    return result

d = {
    'host': '192.168.0.1',
    'name': 'my_new_name',
    'port': 443,
    'something_more': 'hello'
}

desired_key_order = ('name', 'something_more', 'host', 'port')
goal = reorder_items(d, desired_key_order)
print(goal)

结果:

{'name': 'my_new_name', 'something_more': 'hello', 'host': '192.168.0.1', 'port': 443}

(*您也可以在 CPython 3.6 中执行此操作,但这是一个不应依赖的实现细节)

在 3.7 之前,您无法直接控制 dict 中项目的排序方式。但是你可以使用collections.OrderedDict 类型。使用前面代码块中的函数,将result = {} 换成result = collections.OrderedDict()


一种更简洁(尽管有点不透明)的方法是:

result = {key:d[key] for category in (desired_key_order, d.keys()) for key in category if key in d}
#or, for versions prior to 3.7,
result = collections.OrderedDict((key, d[key]) for category in (desired_key_order, d.keys()) for key in category if key in d)

这利用了 dict 推导和 OrderedDict 可以使用重复键构造的事实,同时保持与每个键的第一次出现相关的顺序。

这仍然比 jpp 的解决方案长一点,因为它试图更容错一些。即使d 包含desired_key_order 没有的键,它也可以工作,反之亦然。未指定顺序的项目将出现在结果中具有指定顺序的项目之后。

【讨论】:

  • 或者你可以简单地做goal={k: d[k]for k in ('name', 'something_more', 'host', 'port')}如果在3.7+。
【解决方案2】:

您可以使用列表推导将元组列表提供给collections.OrderedDict

from collections import OrderedDict

d = {'host': '192.168.0.1', 'name': 'my_new_name',
     'port': 443, 'something_more': 'hello'}

key_order = ('name', 'something_more', 'host', 'port')
res = OrderedDict([(k, d[k]) for k in key_order])

OrderedDict([('name', 'my_new_name'),
             ('something_more', 'hello'),
             ('host', '192.168.0.1'),
             ('port', 443)])

适用于 Python 2.x 及更高版本。对于 Python 3.7+,您可以依赖插入排序并使用常规字典。

【讨论】:

  • 如果您确定key_order 包含d 中可能出现的每个键,则此解决方案优于我自己的解决方案。如果d 包含key_order 不包含的键,则它们不会出现在结果中。为了确保结果字典与输入字典的大小相同,我做了一些努力,但如果输入具有可靠且众所周知的结构,这可能是不必要的。
  • @Kevin,是的,您的解决方案肯定更通用。我从 OP 的示例数据中暗示了一点。
【解决方案3】:

如果您使用的是 Python OrderedDict 会保留项目的顺序:

from collections import OrderedDict

d = OrderedDict([
        ('name', 'my_new_name'),
        ('something_more', 'hello'),
        ('host', '192.168.0.1'),
        ('port', 443)
    ])

# OrderedDict([('name', 'my_new_name'), ('something_more', 'hello'), ('host', '192.168.0.1'), ('port', 443)])

【讨论】:

    【解决方案4】:

    Ordered Dictionary 是你需要的东西,我的朋友。]

    from collections import OrderedDict
    d = OrderedDict([
    ('name', 'my_new_name'),
    ('something_more', 'hello'),
    ('host', '192.168.0.1'),
    ('port', 443)
    ])
    

    编辑:这只适用于 Python 3.6+。还为OrderedDict 提供元组列表而不是字典,以维护键值对的顺序。

    【讨论】:

      猜你喜欢
      • 2015-12-22
      • 1970-01-01
      • 2013-07-15
      • 2018-05-08
      • 1970-01-01
      • 2016-10-23
      • 2018-10-22
      • 1970-01-01
      • 2021-12-06
      相关资源
      最近更新 更多