Python 为什么可以';你不能用一个不易损坏的内存地址作为dict的键吗?

Python 为什么可以';你不能用一个不易损坏的内存地址作为dict的键吗?,python,python-2.7,python-3.x,Python,Python 2.7,Python 3.x,我知道,由于列表等不可破坏的类型正在变异,它们不能用作散列的键。然而,我不明白为什么他们的内存地址(我不相信会有变化)可以使用 例如: my_list = [1,2,3] my_dict = {my_list: 1} #error my_dict = {id(my_list): 1} # no error 如果扩展列表、集等,实际上可以将对象的内存地址用作哈希函数 使用内存地址进行哈希的主要原因是,如果两个对象相等(a==b的计算结果为True),我们也希望它们的哈希相等(hash(a)==h

我知道,由于列表等不可破坏的类型正在变异,它们不能用作散列的键。然而,我不明白为什么他们的内存地址(我不相信会有变化)可以使用

例如:

my_list = [1,2,3]
my_dict = {my_list: 1} #error
my_dict = {id(my_list): 1} # no error
如果扩展
列表
等,实际上可以将对象的内存地址用作哈希函数

使用内存地址进行哈希的主要原因是,如果两个对象相等(
a==b
的计算结果为
True
),我们也希望它们的哈希相等(
hash(a)==hash(b)
True
)。否则,我们可能会出现意外行为

要查看此示例,让我们创建自己的类,扩展
list
,并将对象的内存地址用作哈希函数

>>> class HashableList(list):
        def __hash__(self):
            return id(self)  # Returns the memory address of the object
现在我们可以创建两个哈希列表!我们的
HashableList
使用与python内置列表相同的构造函数

>>> a = HashableList((1, 2, 3))
>>> b = HashableList((1, 2, 3))
果然,正如我们所料,我们得到了

>>> a == b
True
我们可以把我们的名单翻个底朝天

>>> hash(a)
1728723187976
>>> hash(b)
1728723187816
>>> hash(a) == hash(b)
False
如果查看最后3位数字,您将看到
a
b
在内存中彼此靠近,但不在同一位置。因为我们使用内存地址作为散列,这也意味着它们的散列不相等

如果比较两个相等元组(或任何其他可哈希对象)的内置哈希,会发生什么

如果您自己尝试,您的
('foo','bar')
哈希值将与我的不匹配,因为每次启动新的python会话时,字符串哈希值都会发生变化。重要的是,在同一会话中,
hash(y)
始终等于
hash(z)

让我们看看如果我们创建一个集合,并使用
HashableList
对象和我们创建的元组会发生什么

>>> s = set()
>>> s.add(a)
>>> s.add(y)
>>> s
{[1, 2, 3], ('foo', 'bar')}

>>> a in s  # Since hash(a) == hash(a), we can find a in our set
True
>>> y in s  # Since hash(y) == hash(y), we can find y in our set
True
>>> b in s
False
>>> z in s
True

即使
a==b
,我们也无法在集合中找到
a
,因为
hash(b)
不等于
hash(a)
,所以我们无法在集合中找到等价的列表

那些是布景,不是口述。Plus
id()
返回一个
int
那么为什么它不起作用呢?你正在对一个整数进行散列运算。你可以,很容易,但是
{some\u list:which}
不会这样做,因为它会破坏dicts的语义。
>>> s = set()
>>> s.add(a)
>>> s.add(y)
>>> s
{[1, 2, 3], ('foo', 'bar')}

>>> a in s  # Since hash(a) == hash(a), we can find a in our set
True
>>> y in s  # Since hash(y) == hash(y), we can find y in our set
True
>>> b in s
False
>>> z in s
True