如何将元素添加到OrderedDict的开头?

use*_*209 68 python dictionary python-2.x python-3.x ordereddict

我有这个:

d1 = OrderedDict([('a', '1'), ('b', '2')])
Run Code Online (Sandbox Code Playgroud)

如果我这样做:

d1.update({'c':'3'})
Run Code Online (Sandbox Code Playgroud)

然后我明白了:

OrderedDict([('a', '1'), ('b', '2'), ('c', '3')])
Run Code Online (Sandbox Code Playgroud)

但我想要这个:

[('c', '3'), ('a', '1'), ('b', '2')]
Run Code Online (Sandbox Code Playgroud)

没有创建新词典.

Ash*_*ary 67

在Python 2中没有内置的方法.如果你需要这个,你需要编写一个prepend()OrderedDictO(1)复杂度的内部操作的方法/函数.

对于Python 3.2及更高版本,您应该使用该move_to_end方法.该方法接受一个last参数,该参数指示元素是否将移动到底部(last=True)或顶部(last=False)OrderedDict.

最后,如果你想要一个快速,肮脏和缓慢的解决方案,你可以OrderedDict从头开始创建一个新的.

四种不同解决方案的详细信息:


扩展OrderedDict并添加新的实例方法

from collections import OrderedDict

class MyOrderedDict(OrderedDict):

    def prepend(self, key, value, dict_setitem=dict.__setitem__):

        root = self._OrderedDict__root
        first = root[1]

        if key in self:
            link = self._OrderedDict__map[key]
            link_prev, link_next, _ = link
            link_prev[1] = link_next
            link_next[0] = link_prev
            link[0] = root
            link[1] = first
            root[1] = first[0] = link
        else:
            root[1] = first[0] = self._OrderedDict__map[key] = [root, first, key]
            dict_setitem(self, key, value)
Run Code Online (Sandbox Code Playgroud)

演示:

>>> d = MyOrderedDict([('a', '1'), ('b', '2')])
>>> d
MyOrderedDict([('a', '1'), ('b', '2')])
>>> d.prepend('c', 100)
>>> d
MyOrderedDict([('c', 100), ('a', '1'), ('b', '2')])
>>> d.prepend('a', d['a'])
>>> d
MyOrderedDict([('a', '1'), ('c', 100), ('b', '2')])
>>> d.prepend('d', 200)
>>> d
MyOrderedDict([('d', 200), ('a', '1'), ('c', 100), ('b', '2')])
Run Code Online (Sandbox Code Playgroud)

操作OrderedDict对象的独立函数

这个函数通过接受dict对象,键和值来做同样的事情.我个人更喜欢上课:

from collections import OrderedDict

def ordered_dict_prepend(dct, key, value, dict_setitem=dict.__setitem__):
    root = dct._OrderedDict__root
    first = root[1]

    if key in dct:
        link = dct._OrderedDict__map[key]
        link_prev, link_next, _ = link
        link_prev[1] = link_next
        link_next[0] = link_prev
        link[0] = root
        link[1] = first
        root[1] = first[0] = link
    else:
        root[1] = first[0] = dct._OrderedDict__map[key] = [root, first, key]
        dict_setitem(dct, key, value)
Run Code Online (Sandbox Code Playgroud)

演示:

>>> d = OrderedDict([('a', '1'), ('b', '2')])
>>> ordered_dict_prepend(d, 'c', 100)
>>> d
OrderedDict([('c', 100), ('a', '1'), ('b', '2')])
>>> ordered_dict_prepend(d, 'a', d['a'])
>>> d
OrderedDict([('a', '1'), ('c', 100), ('b', '2')])
>>> ordered_dict_prepend(d, 'd', 500)
>>> d
OrderedDict([('d', 500), ('a', '1'), ('c', 100), ('b', '2')])
Run Code Online (Sandbox Code Playgroud)

使用OrderedDict.move_to_end()(Python> = 3.2)

Python 3.2引入了该OrderedDict.move_to_end()方法.使用它,我们可以在O(1)时间内将现有密钥移动到字典的任一端.

>>> d1 = OrderedDict([('a', '1'), ('b', '2')])
>>> d1.update({'c':'3'})
>>> d1.move_to_end('c', last=False)
>>> d1
OrderedDict([('c', '3'), ('a', '1'), ('b', '2')])
Run Code Online (Sandbox Code Playgroud)

如果我们需要插入一个元素并将其移到顶部,一步一步,我们可以直接使用它来创建一个prepend()包装器(这里没有介绍).


创造一个新的OrderedDict- 慢!!!

如果您不想这样做并且性能不是问题,那么最简单的方法是创建一个新的dict:

from itertools import chain, ifilterfalse
from collections import OrderedDict


def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in ifilterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

d1 = OrderedDict([('a', '1'), ('b', '2'),('c', 4)])
d2 = OrderedDict([('c', 3), ('e', 5)])   #dict containing items to be added at the front
new_dic = OrderedDict((k, d2.get(k, d1.get(k))) for k in \
                                           unique_everseen(chain(d2, d1)))
print new_dic
Run Code Online (Sandbox Code Playgroud)

输出:

OrderedDict([('c', 3), ('e', 5), ('a', '1'), ('b', '2')])
Run Code Online (Sandbox Code Playgroud)

  • 上面的`ordered_dict_prepend`中必定有一个**bug**.调用`ordered_dict_prepend(d,'c',100)`两次并尝试打印生成的dict(通过在Python的控制台中输入`d`)会导致Python进程继续占用内存.用Python 2.7.10测试 (2认同)

Jar*_*red 14

编辑(2019-02-03) 请注意,以下答案仅适用于旧版本的Python.最近,OrderedDict已经用C重写了.此外,这确实触及了双下划线属性,这是不赞成的.

OrderedDict为了类似的目的,我只是在我的一个项目中编写了一个子类.这是要点.

O(1)与大多数这些解决方案不同,插入操作也是固定时间(它们不需要您重建数据结构).

>>> d1 = ListDict([('a', '1'), ('b', '2')])
>>> d1.insert_before('a', ('c', 3))
>>> d1
ListDict([('c', 3), ('a', '1'), ('b', '2')])
Run Code Online (Sandbox Code Playgroud)

  • 这不再起作用,因为`OrderedDict`已经[在C中重写](https://bugs.python.org/issue16991)从Python 3.5开始,并且这个子类犯了与内部结构混淆的禁忌(实际上是将名称重写为访问__属性). (2认同)

dol*_*hin 12

你必须创建一个新的实例OrderedDict.如果您的钥匙是唯一的:

d1=OrderedDict([("a",1),("b",2)])
d2=OrderedDict([("c",3),("d",99)])
both=OrderedDict(list(d2.items()) + list(d1.items()))
print(both)

#OrderedDict([('c', 3), ('d', 99), ('a', 1), ('b', 2)])
Run Code Online (Sandbox Code Playgroud)

但如果没有,请注意,您可能需要或不需要此行为:

d1=OrderedDict([("a",1),("b",2)])
d2=OrderedDict([("c",3),("b",99)])
both=OrderedDict(list(d2.items()) + list(d1.items()))
print(both)

#OrderedDict([('c', 3), ('b', 2), ('a', 1)])
Run Code Online (Sandbox Code Playgroud)

  • 在python3中,items方法不再返回列表,而是返回一个视图,它就像一个集合.在这种情况下,你需要采用set union,因为与+连接将不起作用:dict(x.items()| y.items()) (3认同)

Ter*_*edy 6

如果您知道需要'c'键但不知道该值,则在创建dict时插入带有虚拟值的'c'.

d1 = OrderedDict([('c', None), ('a', '1'), ('b', '2')])
Run Code Online (Sandbox Code Playgroud)

并在以后更改该值.

d1['c'] = 3
Run Code Online (Sandbox Code Playgroud)


sim*_*imP 5

现在可以使用 move_to_end(key, last=True)

>>> d = OrderedDict.fromkeys('abcde')
>>> d.move_to_end('b')
>>> ''.join(d.keys())
'acdeb'
>>> d.move_to_end('b', last=False)
>>> ''.join(d.keys())
'bacde'
Run Code Online (Sandbox Code Playgroud)

https://docs.python.org/3/library/collections.html#collections.OrderedDict.move_to_end