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

如何解决Python WeakKeyDictionary无法存储不可哈希类型的问题?

如何在WeakKeyDictionary中存储不可哈希类型?

正如cpython issue 88306中指出的,Python的weakref.WeakKeyDictionary无法处理不可哈希类型。相关讨论认为这是不必要的限制——在WeakKeyDictionary这个特殊场景下,用对象的id而非哈希值作为键的唯一标识完全可行,因为原对象被垃圾回收时,对应的键会自动从字典中移除。

解决方案:自定义WeakKeyIdDictionary

我们可以通过继承weakref.WeakKeyDictionary并修改键的处理逻辑来实现需求,核心是用两个包装类来基于对象id生成可哈希的弱引用键:

import weakref
import typing
import typing_extensions

class _IdKey:
    def __init__(self, key):
        self._id = id(key)

    def __hash__(self):
        return self._id

    def __eq__(self, other: typing_extensions.Self):
        return self._id == other._id

    def __repr__(self):
        return f"<_IdKey(_id={self._id})>"


class _IdWeakRef(_IdKey):
    def __init__(self, key, remove: typing.Callable[[typing.Any], None]):
        super().__init__(key)
        # 持有弱引用,避免移除回调被垃圾回收
        self._ref = weakref.ref(key, lambda _: remove(self))

    def __call__(self):
        # 供weakref.WeakKeyDictionary.__copy__调用
        return self._ref()

    def __repr__(self):
        return f"<_IdKey(_id={self._id},{self._ref})>"

class WeakKeyIdDictionary(weakref.WeakKeyDictionary):
    """
    重写所有涉及字典键访问的方法,用_id作为哈希依据
    """
    # 核心逻辑是将传入的键包装为_IdKey/_IdWeakRef实例后再操作底层字典
    # 需补充实现__getitem__、__setitem__、__contains__等方法

需要注意的是,这个实现依赖Python 3.10版本WeakKeyDictionary的内部细节,在其他Python版本中可能失效。如果需要跨版本兼容,建议完全重写一个全新的类,而非继承官方实现。

不可行的方案说明

  • 为目标类实现自定义__hash__方法:这种方式对外部代码无效,且在WeakKeyDictionary以外的场景中,基于id的哈希逻辑并不可靠。
  • 猴子补丁修改__hash__:对Python内置类无法生效,还会影响其他依赖该类哈希逻辑的代码,引发意外问题。

内容的提问来源于stack exchange,提问作者Peter Barmettler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 07:10:28