暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

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

大码怪 2021-02-22
530

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

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

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

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

有序字典 OrderedDict

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

但是 OrderedDict
dict
还是有一些不同:

  • 常规的 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
方法,返回一个空的 list
list.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_factory
int
,使 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_factory
set
使 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})]


文章转载自大码怪,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论