有序字典 和 带默认值的字典

从3.6开始字典都是有序的,并且从3.7开始这成了正式的Python语言特性!!!!!
实现原理 多维护了一个index的列表 用来对应数据存入hashtable的顺序
比如 k1第一个入hashtable 他在table里的索引是2
那么维护的index列表为 [None, None, 0, None, None ......]
k2在hashtable里的索引是0
index列表 [1, None, 0, None, None ......]

有序字典 和 带默认值的字典

collections.OrderedDict 是字典的子类,保存了他们被添加的顺序。

collections.defaultdict 是字典的子类,提供了一个工厂函数,为字典查询提供一个默认值。

以下示例基本来自Python3官方文档,有兴趣的同学可以去看原文档。

有序字典 OrderedDict

注:Python 3.7及以后,默认的 dict 就已经是有序的了,所以 OrderedDict 的作用没有以前那么重要了。

但是 OrderedDictdict 还是有一些不同:

  • 常规的 dict 被设计为非常擅长映射操作。 跟踪插入顺序是次要的。
  • OrderedDict 旨在擅长重新排序操作。 空间效率、迭代速度和更新操作的性能是次要的。
  • 算法上, OrderedDict 可以比 dict 更好地处理频繁的重新排序操作。 这使其适用于跟踪最近的访问(例如在 LRU cache 中)。
  • 对于 OrderedDict ,相等操作(即==)检查匹配顺序。
In [190]: a={'a': 3, 'b': 2}

In [191]: b={'b': 2, 'a': 3}

In [192]: a
Out[192]: {'a': 3, 'b': 2}

In [193]: b
Out[193]: {'b': 2, 'a': 3}

In [194]: a==b
Out[194]: True

In [195]: c=OrderedDict({'a': 3, 'b': 2})

In [196]: d=OrderedDict({'b': 2, 'a': 3})

In [197]: c==d
Out[197]: False
  • OrderedDict 类的 popitem() 方法有不同的签名。它接受一个可选参数来指定弹出哪个元素。
OrderedDict.popitem(last=True)
dict 的 popitem() 方法 无需参数
  • OrderedDict 类有一个 move_to_end() 方法,可以有效地将元素移动到任一端。
OrderedDict 的实例方法
  • popitem(last=True):有序字典的 popitem() 方法移除并返回一个 (key, value) 键值对。 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。

    即:默认弹出最后一个元素的键值对。

In [211]: c=OrderedDict({'a': 3, 'b': 2})

In [212]: c.popitem(True)
Out[212]: ('b', 2)

In [213]: c
Out[213]: OrderedDict([('a', 3)])
  • move_to_end(key, last=True):将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头。如果 key 不存在则会触发 KeyError
In [214]: d = OrderedDict.fromkeys('abcde')

In [215]: d
Out[215]: OrderedDict([('a', None), ('b', None), ('c', None), ('d', None), ('e', None)])

# 将b移到末尾
In [216]: d.move_to_end('b')

In [217]: d
Out[217]: OrderedDict([('a', None), ('c', None), ('d', None), ('e', None), ('b', None)])

# 将b移到开头
In [218]: d.move_to_end('b', last=False)

In [219]: d
Out[219]: OrderedDict([('b', None), ('a', None), ('c', None), ('d', None), ('e', None)])
  • OrderedDict 的项(item),键(key)和值(value) 视图 现在支持逆序迭代,通过 reversed() 。
In [228]: d = OrderedDict.fromkeys('abcde')

In [229]: d
Out[229]: OrderedDict([('a', None), ('b', None), ('c', None), ('d', None), ('e', None)])

In [230]: list(reversed(d.items()))
Out[230]: [('e', None), ('d', None), ('c', None), ('b', None), ('a', None)]

In [231]: list(reversed(d.keys()))
Out[231]: ['e', 'd', 'c', 'b', 'a']

In [232]: list(reversed(d.values()))
Out[232]: [None, None, None, None, None]
OrderedDict 例子和用法
  1. 如果新条目覆盖现有条目,则原始插入位置将更改并移至末尾:
class LastUpdatedOrderedDict(OrderedDict):
    'Store items in the order the keys were last added'

    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        self.move_to_end(key)
In [242]: l = LastUpdatedOrderedDict.fromkeys('abcde')

In [243]: l
Out[243]: 
LastUpdatedOrderedDict([('a', None),
                        ('b', None),
                        ('c', None),
                        ('d', None),
                        ('e', None)])

In [244]: l['a'] = 9

In [245]: l
Out[245]: 
LastUpdatedOrderedDict([('b', None),
                        ('c', None),
                        ('d', None),
                        ('e', None),
                        ('a', 9)])
  1. 一个 OrderedDict 对于实现 functools.lru_cache() 的变体也很有用:
class LRU(OrderedDict):
    'Limit size, evicting the least recently looked-up key when full'

    def __init__(self, maxsize=128, /, *args, **kwds):
        self.maxsize = maxsize
        super().__init__(*args, **kwds)

    def __getitem__(self, key):
        value = super().__getitem__(key)
        self.move_to_end(key)
        return value

    def __setitem__(self, key, value):
        if key in self:
            self.move_to_end(key)
        super().__setitem__(key, value)
        if len(self) > self.maxsize:
            oldest = next(iter(self))
            del self[oldest]
a = LRU(2)
a["a"] = 5
print(a)
a["b"] = 5
print(a)
a["c"] = 5
print(a)

结果:
LRU([('a', 5)])
LRU([('a', 5), ('b', 5)])
LRU([('b', 5), ('c', 5)])

defaultdict 对象

collections.defaultdict([default_factory[, ...]])

对象包含一个名为 default_factory 的属性,构造时,第一个参数用于为该属性提供初始值,默认为 None。所有其他参数(包括关键字参数)都相当于传递给 dict 的构造函数。

defaultdict 例子

使用 list 作为 default_factory,很轻松地将(键-值对组成的)序列转换为(键-列表组成的)字典:

In [250]: s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]

In [251]: d = defaultdict(list)

In [252]: for k, v in s:
     ...:     d[k].append(v)
     ...: 

In [253]: d
Out[253]: defaultdict(list, {'yellow': [1, 3], 'blue': [2, 4], 'red': [1]})

In [254]: sorted(d.items())
Out[254]: [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

当每个键第一次遇见时,它还没有在字典里面,所以自动创建该条目,即调用 default_factory 方法,返回一个空的 listlist.append() 操作添加值到这个新的列表里。当再次存取该键时,就正常操作,list.append() 添加另一个值到列表中。这个计数比它的等价方法 dict.setdefault() 要快速和简单:

In [255]: d = {}

In [256]: for k, v in s:
     ...:     d.setdefault(k, []).append(v)
     ...: 

In [257]: sorted(d.items())
Out[257]: [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

设置 default_factoryint,使 defaultdict 用于计数:

In [258]: s = 'mississippi'

In [259]: d = defaultdict(int)

In [260]: for k in s:
     ...:     d[k] += 1
     ...: 

In [261]: sorted(d.items())
Out[261]: [('i', 4), ('m', 1), ('p', 2), ('s', 4)]

当一个字母首次遇到时,它会查询失败,则 default_factory 会调用 int() 来提供一个整数 0 作为默认值。后续的自增操作建立起对每个字母的计数。

函数 int() 总是返回 0,这是常数函数的特殊情况。一个更快和灵活的方法是使用 lambda 函数,可以提供任何常量值(不只是0):

# 可以理解为 字典的默认值为 "<missing>"
In [263]: def constant_factory(value):
     ...:     return lambda: value
     ...: 

In [264]: d = defaultdict(constant_factory('<missing>'))

In [265]: d
Out[265]: defaultdict(<function __main__.constant_factory.<locals>.<lambda>()>, {})

In [266]: d.update(name='John', action='ran')

In [267]: d
Out[267]: 
defaultdict(<function __main__.constant_factory.<locals>.<lambda>()>,
            {'name': 'John', 'action': 'ran'})

In [268]: '%(name)s %(action)s to %(object)s' % d
Out[268]: 'John ran to <missing>'

设置 default_factoryset 使 defaultdict 用于构建 set 集合:

In [272]: s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]

In [273]: d = defaultdict(set)

In [274]: for k, v in s:
     ...:     d[k].add(v)
     ...: 

In [275]: d
Out[275]: defaultdict(set, {'red': {1, 3}, 'blue': {2, 4}})

In [276]: sorted(d.items())
Out[276]: [('blue', {2, 4}), ('red', {1, 3})]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。