Python3.7+如何按顺序访问字典元素?
问题描述
我了解自Python 3.7起字典是有序的,但文档似乎未列出利用该顺序的方法。比如如何不依赖键按顺序访问字典的第一个元素?有序字典可执行哪些操作?
举例来说,我正在实现LFU(最近最少使用)缓存,不仅需要跟踪键的使用次数,还要用LRU(最近最少使用)信息作为平局决胜条件。我可以用字典嵌套队列实现优先队列,但会失去队列内的O(1)查找能力。若使用字典嵌套字典,既能保留哈希集合的优势,又能实现优先队列……我认为是可行的。我只需要能弹出字典的第一个元素,但遗憾的是没有
dict.popleft()方法。目前我将键转换为列表并使用列表的第一个元素,这确实有效(字典会维持顺序),但转换成本较高。
LFU_queue = collections.defaultdict(collections.defaultdict) LFU_queue[1].update({"key_1":None}) LFU_queue[1].update({"key_32":None}) LFU_queue[1].update({"key_6":None}) # 查看该结构,得到预期的有序嵌套字典: # { 1: {"key_1":None, "key_32":None, "key_6":None}} # 我希望能执行类似这样的操作 # LFU_queue[1].popleft() 以返回 {"key_1":None} list(LFU_queue[1])[0] 可行,但不够理想
解决方案说明
1. 高效访问/弹出字典首个元素的方法
Python 3.7+ 的普通字典确实保留插入顺序,但没有直接提供popleft()方法。如果不想承担转列表的O(n)成本,可以用以下O(1)级别的操作:
- 获取首个键:用
next(iter(dict))直接获取字典迭代器的第一个元素,比如next(iter(LFU_queue[1]))就能拿到key_1,再通过键取值即可。 - 弹出首个元素:结合迭代器和
pop()方法:inner_dict = LFU_queue[1] first_key = next(iter(inner_dict)) first_item = {first_key: inner_dict.pop(first_key)} # 此时first_item就是{"key_1": None},且该元素已从inner_dict中移除
2. 有序字典的可用操作
对于Python 3.7+的普通有序字典,除常规字典操作外,还能基于顺序做这些事:
- 用
for k, v in dict.items()按插入顺序遍历键值对 - 用
reversed(dict)按逆插入顺序迭代键(Python 3.8及以上支持) - 用
next(iter(dict))和next(reversed(dict))快速获取首尾键
如果需要更专业的顺序操作(比如直接弹出首尾、移动元素位置),建议使用collections.OrderedDict,它专门提供了这类方法:
from collections import OrderedDict od = OrderedDict([("key_1", None), ("key_32", None), ("key_6", None)]) first_item = od.popitem(last=False) # 弹出第一个元素,返回("key_1", None) od.move_to_end("key_32") # 将指定键移到字典末尾
3. LFU缓存实现的优化建议
你的嵌套字典思路完全可行:外层字典记录使用次数,内层有序字典按LRU顺序存储同次数的键。如果用普通字典,就用next(iter(inner_dict))获取最久未使用的键;如果追求代码简洁和操作直观,内层换成OrderedDict会更方便,直接调用popitem(last=False)就能弹出LRU元素,同时保持O(1)的查找效率。
内容的提问来源于stack exchange,提问作者Mister Nibbles

