UserDict.popitem非LIFO行为疑问:为何弹出首个元素而非最后一个?
UserDict.popitem 弹出第一个元素而非最后一个的原因分析
请查看以下代码片段:
from collections import UserDict class D(dict): ... d = D(foo="bar", baz=42) print(d.popitem()) # ('baz', 42) class UD(UserDict): ... ud = UD(foo="bar", baz=42) print(ud.data) # {'foo': 'bar', 'baz': 42} print(ud.popitem()) # ('foo', 'bar')
UserDict的实现保留插入顺序符合预期,因为其内部的data字典自Python 3.7起就是有序的。但为何UserDict.popitem()弹出的是第一个元素而非最后一个?这是bug还是特性?
已在全新环境的Python 3.13.9和3.14.0版本中测试。
这是Python 3.13/3.14中UserDict.popitem方法的实现bug:方法内的参数判断逻辑与官方文档描述完全相反。
根据官方文档定义,UserDict.popitem(last=True)应按LIFO(后进先出)顺序弹出最后插入的元素,last=False按FIFO(先进先出)顺序弹出第一个插入的元素。但实际代码中逻辑被写反了:
- 默认
last=True时,代码执行next(iter(self)),获取并弹出第一个插入的元素 - 传入
last=False时,代码执行next(reversed(self)),获取并弹出最后插入的元素
你可以通过显式传参验证这一矛盾:
ud = UD(foo="bar", baz=42) print(ud.popitem(last=False)) # ('baz', 42),弹出最后一个元素 print(ud.popitem(last=True)) # ('foo', 'bar'),弹出第一个元素
若需要和普通dict一致的默认LIFO行为,可通过两种方式解决:
- 直接调用内部
data字典的popitem方法:ud.data.popitem() - 自定义子类重写
popitem方法修正逻辑:
from collections import UserDict from typing import TypeVar, Tuple K = TypeVar('K') V = TypeVar('V') class FixedUD(UserDict[K, V]): def popitem(self, last: bool = True) -> Tuple[K, V]: if not self: raise KeyError("popitem(): dictionary is empty") # 修正逻辑:last=True取逆序第一个(最后插入元素),last=False取正序第一个 key = next(reversed(self)) if last else next(iter(self)) return key, self.pop(key)
这个bug大概率会在后续Python版本中被修复。
内容的提问来源于stack exchange,提问作者maejam
相关产品推荐
相关产品推荐

