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

从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})]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341