可洗,不变

joa*_*uin 76 python hash immutability data-structures

从最近的SO问题(参见在python中创建一个由列表索引的字典)我意识到我可能对python中可散列和不可变对象的含义有一个错误的概念.

  • 在实践中,什么是可以平均的?
  • hashable和immutable之间的关系是什么?
  • 是否有可变对象是可清洗的或不可变的对象?

mae*_*ics 84

散列是以可重复的方式将大量数据转换为更小量(通常是单个整数)的过程,以便可以在常量时间(O(1))中查找表,这对于高性能非常重要算法和数据结构.

不可变性是指对象在创建后不会以某种重要方式改变的想法,尤其是以任何可能改变该对象的哈希值的方式.

这两个想法是相关的,因为用作散列键的对象通常必须是不可变的,因此它们的散列值不会改变.如果允许更改,则该对象在诸如散列表之类的数据结构中的位置将改变,然后用于效率的散列的整个目的被打败.

要真正掌握这个想法,您应该尝试使用C/C++等语言实现自己的哈希表,或者阅读HashMap该类的Java实现.

  • 此外,哈希表不可能检测其键的哈希值何时发生变化(至少以任何有效的方式)。这是一个常见的陷阱,例如在 Java 中,如果您修改用作其中的键的对象,`HashMap` 就会损坏:既不能找到旧键也不能找到新键,即使您打印地图,也可以在那里看到它。 (3认同)

liu*_*ihe 11

  • 是否有可变对象是可清洗的或不可变的对象?

在Python中,元组是不可变的,但只有当它的所有元素都是可哈希的时候才可以使用它.

>>> tt = (1, 2, (30, 40))
>>> hash(tt)
8027212646858338501
>>> tl = (1, 2, [30, 40])
>>> hash(tl)
TypeError: unhashable type: 'list'
Run Code Online (Sandbox Code Playgroud)

可洗类型

  • 原子不可变类型都是可清除的,例如str,bytes,numeric类型
  • 冻结集总是可以清除的(根据定义,其元素必须是可清除的)
  • 元组只有在其所有元素都可以清洗时才可以清洗
  • 用户定义的类型默认是可哈希的,因为它们的哈希值是它们的id()


And*_*ffe 8

从技术上讲,hashable意味着类定义__hash__().根据文件:

__hash__()应该返回一个整数.唯一需要的属性是比较相等的对象具有相同的哈希值; 建议以某种方式将对象的组件的哈希值混合在一起(例如,使用异或),这些哈希值也在对象的比较中起作用.

我认为对于Python内置类型,所有可清除类型也是不可变的.

拥有一个仍然定义的可变对象将是困难的,但也许并非不可能__hash__().

  • 我认为答案有点误导,`list`定义`__hash__`,意思是`hasattr([1,2,3],"_ _ hash __")`返回'True`,但是调用`hash([1,2] ,3])`引发了一个`TypeError`(Python 3),所以它不是完全可以删除的.依赖于`__hash__`的存在不足以确定某些东西是否是a)hashable b)不可变的 (4认同)
  • 值得注意的是`__hash__`默认定义为返回对象的`id`;你必须不遗余力地设置`__hash__ = None`以使其不可散列。另外,正如 Mark Ransom 提到的,还有一个额外的条件,即只有当哈希值永远不会改变时,它才是可哈希的! (2认同)

Mar*_*som 7

Python词汇表:

如果一个对象具有一个在其生命周期内永远不会改变的哈希值(它需要一个__hash__()方法),并且可以与其他对象(它需要一个__eq__()或多个__cmp__()方法)进行比较,则该对象是可清除的.比较相等的可哈希对象必须具有相同的哈希值.

Hashability使对象可用作字典键和set成员,因为这些数据结构在内部使用哈希值.

所有Python的不可变内置对象都是可清除的,而没有可变容器(例如列表或字典).默认情况下,作为用户定义类实例的对象是可清除的; 他们都比较不相等,他们的哈希值是他们的id().

Dicts和sets必须使用哈希来在哈希表中进行有效查找; 散列值必须是不可变的,因为更改散列会弄乱数据结构并导致dict或set失败.使哈希值不可变的最简单方法是使整个对象不可变,这就是为什么这两个经常被一起提到的原因.

虽然没有内置的可变对象是可以清除的,但是可以使用不可变的哈希值来创建一个可变对象.通常只有对象的一部分表示其标识,而对象的其余部分包含可自由更改的属性.只要哈希值和比较函数基于标识但不基于可变属性,并且标识永远不会更改,您就满足了要求.

  • 用户定义的类默认定义可散列类型(散列只是对象的`id`).这在对象的生命周期中不会改变,因此它是可以清除的,但这并不意味着你不能定义可变类型!对不起,但可靠性并不意味着不变性. (12认同)
  • 注意:[Python 术语表](https://docs.python.org/3/glossary.html) 已更新。并非所有内置对象都是可哈希的。“不可变容器(例如元组和冻结集)仅当其元素可哈希时才可哈希。” (3认同)