猿问

在 dict/set 中查找不可散列对象期间出现

前言

我知道dicts/ sets 应该只由于它们的实现而用可散列对象创建/更新,所以当这种代码失败时


>>> {{}}  # empty dict of empty dict

Traceback (most recent call last):

  File "<input>", line 1, in <module>

TypeError: unhashable type: 'dict'

没关系,我已经看到大量此类消息。


但是如果我想检查一些不可散列的对象是否在set/dict


>>> {} in {}  # empty dict not in empty dict

我也收到错误


Traceback (most recent call last):

  File "<input>", line 1, in <module>

TypeError: unhashable type: 'dict'

问题

这种行为背后的原因是什么?我知道查找和更新可能在逻辑上是相连的(就像在dict.setdefaultmethod 中一样),但它不应该在修改步骤而不是查找中失败吗?也许我有一些我以某种方式处理的可散列的“特殊”值,但其他(可能无法散列) - 在另一个中:


SPECIAL_CASES = frozenset(range(10)) | frozenset(range(100, 200))

...

def process_json(obj):

    if obj in SPECIAL_CASES:

        ...  # handle special cases

    else:

        ...  # do something else

所以对于给定的查找行为,我被迫使用其中一个选项


LBYL方式:检查是否obj是可散列的,然后才检查它是否是其中之一SPECIAL_CASES(这不是很好,因为它基于SPECIAL_CASES结构和查找机制限制,但可以封装在单独的谓词中),

EAFP方式:使用某种实用程序进行“安全查找”,例如


def safe_contains(dict_or_set, obj):

    try:

        return obj in dict_or_set

    except TypeError:

        return False

使用list/ tuplefor SPECIAL_CASES(不在O(1)查找中)。

还是我错过了一些微不足道的东西?


holdtom
浏览 213回答 2
2回答

慕容708150

我在 Python 错误跟踪器上发现了这个问题。长话短说:如果>>>&nbsp;set([1,2])&nbsp;in&nbsp;{frozenset([1,2]):&nbsp;'a'}返回False它将在某种程度上违反直觉,因为值相等>>>&nbsp;set([1,2])&nbsp;==&nbsp;frozenset([1,2]) True所以我想我会在可能发生这种情况的地方编写和使用适当的实用程序。关于错误的根源:在CPython repo&nbsp;dict___contains__函数(它是一个dict.__contains__方法实现)中调用PyObject_Hash函数(对应于hash函数)-> 对于不可散列的对象(如{}我们的第一种情况)调用PyObject_HashNotImplemented函数-> 生成此错误。

jeck猫

毫无疑问,您已经意识到,集合和字典的内部工作原理非常相似。基本上这个概念是你有键 - 值对(或只是带有集合的键),并且键永远不能改变(不可变)。如果一个对象是可变的,散列就会失去它作为底层数据的唯一标识符的意义。如果你不能判断一个对象是否唯一,一组唯一键的含义就失去了它的唯一性的关键属性。这就是为什么在集合中不允许可变类型和作为字典的键的原因。以你的例子:{} in {} &nbsp;# empty dict not in empty dict我认为你有一点误解,因为dict.__contains__只检查字典的键,而不是值。由于您永远不能将 dict 作为键(因为它是可变的),因此这是无效的。
随时随地看视频慕课网APP

相关分类

Python
我要回答