美文网首页
OrderedDict 有序字典

OrderedDict 有序字典

作者: RayRaymond | 来源:发表于2020-05-06 15:47 被阅读0次

OrderedDict内部维护了一个双向链表,它会根据元素加入的顺序来排列键的位置。第一个新加入的元素被放置在链表的末尾,接下来对已存在的键做重新赋值,不会改变键的位置。
请注意:OrderedDict的大小是普通字典的2倍。这是由于它额外创建的链表所致。

OrderedDict 源码

# pre 和 next 属性作为前驱节点和后驱节点,
# key 储存键,__weakref__支持弱引用
class _Link(object):
    __slots__ = 'prev', 'next', 'key', '__weakref__'
    
class OrderedDict(dict):
    '保留插入顺序的字典'
    # 继承自字典的键值对字典
    # 方法: __getitem__, __len__, __contains__, get
    # The remaining methods are order-aware.
    # 时间复杂度和正常字典一样

    # 内部的 self.__map 字典把 key 和双向链表指针关联
    # 循环双链表以一个哨兵元素开始和结束
    # 哨兵元素永远不会被删除 (简化算法)
    # The sentinel is in self.__hardroot with a weakref proxy in self.__root.
    # The prev links are weakref proxies (to prevent circular references).
    # Individual links are kept alive by the hard reference in self.__map.
    # Those hard references disappear when a key is deleted from an OrderedDict.

    def __init__(self, other=(), /, **kwds):
        '''Initialize an ordered dictionary.  The signature is the same as
        regular dictionaries.  Keyword argument order is preserved.
        '''
        try:
            self.__root
        except AttributeError:
            self.__hardroot = _Link()
            self.__root = root = _proxy(self.__hardroot)
            root.prev = root.next = root
            self.__map = {}
        self.__update(other, **kwds)

    def __setitem__(self, key, value,
                    dict_setitem=dict.__setitem__, 
                    proxy=_proxy, Link=_Link):
        'od.__setitem__(i, y) <==> od[i]=y'
        # Setting a new item creates a new link at the end of the linked list,
        # and the inherited dictionary is updated with the new key/value pair.
        '添加新元素的时候会在链表的末尾创建新链表,并使用新的 键值对 更新被继承的字典'
        if key not in self:
            self.__map[key] = link = Link()
            root = self.__root # 哨兵节点
            last = root.prev 
            '''
            将root节点作为尾节点,创建新节点,
            并更新root节点的前驱节点的后继节点和root节点的前驱节点的指向, 
            形象一点就是在root 节点前不断的对新节点进行前插(基本链表算法)
            '''
            link.prev, link.next, link.key = last, root, key
            last.next = link
            root.prev = proxy(link)
        dict_setitem(self, key, value)

    def __delitem__(self, key, dict_delitem=dict.__delitem__):
        'od.__delitem__(y) <==> del od[y]'
        # Deleting an existing item uses self.__map to find the link which gets
        # removed by updating the links in the predecessor and successor nodes.
        dict_delitem(self, key)
        link = self.__map.pop(key)
        link_prev = link.prev
        link_next = link.next
        link_prev.next = link_next
        link_next.prev = link_prev
        link.prev = None
        link.next = None

''' 移除并返回一个键值对,
    参数 last 为真就采用 LIFO ,也就是最后一个键值对;
    为假就采用 FIFO ,也就是第一个键值对。'''
    def popitem(self, last=True):
        '''Remove and return a (key, value) pair from the dictionary.
        Pairs are returned in LIFO order if last is true or FIFO order if false.
        '''
        if not self:
            raise KeyError('dictionary is empty')
        root = self.__root
        if last:
            link = root.prev
            link_prev = link.prev
            link_prev.next = root
            root.prev = link_prev
        else:
            link = root.next
            link_next = link.next
            root.next = link_next
            link_next.prev = root
        key = link.key
        del self.__map[key]
        value = dict.pop(self, key)
        return key, value

    def move_to_end(self, key, last=True):
        '''Move an existing element to the end (or beginning if last is false).
        Raise KeyError if the element does not exist.
        '''
        link = self.__map[key]
        link_prev = link.prev
        link_next = link.next
        soft_link = link_next.prev
        link_prev.next = link_next
        link_next.prev = link_prev
        root = self.__root
        if last:
            last = root.prev
            link.prev = last
            link.next = root
            root.prev = soft_link
            last.next = link
        else:
            first = root.next
            link.prev = root
            link.next = first
            first.prev = soft_link
            root.next = link

使用有序字典

1. 创建 collections.OrderedDict()

import collections

dic = collections.OrderedDict()
dic['k1'] = 'v1'
dic['k2'] = 'v2'
dic['k3'] = 'v3'
print(dic)

2. 清空 dic.clear()

dic.clear()

3. 拷贝 dic.copy()

dic_copy = dic.copy()

4. fromkeys( key_list, value)

import collections

dic = collections.OrderedDict()        # OrderedDict()
name = ['A','B','C']
new_dic = dic.fromkeys(name)           # OrderedDict([('A', None), ('B', None), ('C', None)])
new_dic_val = dic.fromkeys(name,777)   # OrderedDict([('A', 777), ('B', 777), ('C', 777)])

print(dic)
print(new_dic)
print(new_dic_val)

5. 键值对的列表items() / 键keys() / 值 values()

import collections

dic = collections.OrderedDict()
dic['k1'] = 'v1'
dic['k2'] = 'v2'
print(dic.items())   # odict_items([('k1', 'v1'), ('k2', 'v2')])
print(dic.keys())    # odict_keys(['k1', 'k2'])
print(dic.values())  # odict_values(['v1', 'v2'])

6. move_to_end(key)(指定一个key,把对应的key-value移到最后)

import collections

dic = collections.OrderedDict()
dic['k1'] = 'v1'
dic['k2'] = 'v2'
dic.move_to_end('k1')
print(dic)

# OrderedDict([('k2', 'v2'), ('k1', 'v1')])

7. pop(key)(获取指定key的value,删除)

import collections

dic = collections.OrderedDict()
dic['k1'] = 'v1'
dic['k2'] = 'v2'
dic['k3'] = 'v3'
k = dic.pop('k2')
print(k,dic)

#v2 OrderedDict([('k1', 'v1'), ('k3', 'v3')])

8.popitem() (后进先出弹出最后元素,返回 key 和 value)

import collections

dic = collections.OrderedDict()
dic['k1'] = 'v1'
dic['k2'] = 'v2'
dic['k3'] = 'v3'
print(dic.popitem(),dic)
print(dic.popitem(),dic)

# ('k3', 'v3') OrderedDict([('k1', 'v1'), ('k2', 'v2')])
# ('k2', 'v2') OrderedDict([('k1', 'v1')])

9.setdefault(key)(获取指定key的value,如果key不存在,则创建)

import collections

dic = collections.OrderedDict()
dic['k1'] = 'v1'
dic['k2'] = 'v2'
dic['k3'] = 'v3'
val = dic.setdefault('k5')
print(val,dic)

# None OrderedDict([('k1', 'v1'), ('k2', 'v2'), ('k3', 'v3'), ('k5', None)])

Reference

[1] 关于ordereddict的源代码,再谈,OrderedDict,源码

相关文章

网友评论

      本文标题:OrderedDict 有序字典

      本文链接:https://www.haomeiwen.com/subject/lijightx.html