You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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行为,可通过两种方式解决:

  1. 直接调用内部data字典的popitem方法:ud.data.popitem()
  2. 自定义子类重写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 12:14:51