关于python中元组、字典的可变性。

发布于 2022-09-11 15:32:01 字数 562 浏览 32 评论 0

对可变对象的疑虑点,请各位大佬指点迷津,多谢!

问题描述:

  1. 元组tuple的元素只能容纳不可变对象,且其中的元素可以是列表list,因为实际存放的是列表(可变对象)的地址,只要这个可变列表的地址不变,就算列表中的元素变化了,也不能说是元组元素的变动。
  2. 字典dict的key不能是可变元素,因为需要用key去hash取值。如果此时key为列表(可变对象),则字典会报错:TypeError: unhashable type: 'list'

那么问题来了:
同样是可变元素list,为什么在元组tuple里面作为元素值是合法的、是取地址的、是不可变的,但是在字典dict里面作为key的值就是非法的、是取的、是可变的。

这样的设计,感觉不符合使用上的一致性或直观的理解性吧,因为规则不一致。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

想你的星星会说话 2022-09-18 15:32:01

你想当然地对“不可变序列”这个词语产生了误解。
首先我们要理解,何为“不可变对象”。

objects whose value is unchangeable once they are created are called immutable.
翻译过来,如果一个对象的value不可改变,那么该对象就是“不可变”的。

然后我们思考下,序列的value是什么东西。

Some objects contain references to other objects; these are called containers. Examples of containers are tuples, lists and dictionaries. The references are part of a container's value.
翻译过来,序列/容器的value就是该序列中的“元素的id”。是元素的id,而不是元素的value,懂了吗?还不懂就看python官方手册的《Data model》章节。

>>> foo=([1,2], [3,4]) #包含可变对象的【不可变序列】
>>> [id(i) for i in foo] #之中所有元素的id,就是【不可变序列】的value
[1747679411912, 1747679411848]
>>> foo[0][0]=5 #修改【不可变序列】的元素的value,而非修改【不可变序列】的value
>>> foo
([5, 2], [3, 4])
>>> [id(i) for i in foo] #【不可变序列】的value没有改变
[1747679411912, 1747679411848]
雾里花 2022-09-18 15:32:01

能否hash根据这些元素的散列函数计算的,而不是内容(值)。

在Python的文档词汇表中查看此段落。

某些东西是否可以散列,以及它是如何散列的,取决于其.__hash__()方法的实现。Python本身并不知道对象的可变性。

具体而言, tuple恰好基于其元素散列自身,而list根本没有散列函数 - .__hash__() 方法没有被实现(并且有充分的理由)。这就是为什么如果tuple里包含有list类型的元素, 则tuple 是不能哈希的。

Python 文档

默认情况下,用户定义的类具有__eq__()__hash__()方法;
比较时,所有对象都比较是否相等(除了它们自己), 如x.__hash__()能返回一个适当的值,这时,x == y意味着 x is y 且 hash(x) == hash(y)。

这也是为什么你不必为你的类定义.__hash__() - 在这种情况下Python自动为你定义。但默认实现并不会将实例字段值考虑进去。这就是为什么您可以更改对象内部的值而不更改其哈希值的原因。

自定义类的哈希函数的默认(CPython)实现依赖于一个对象 id(),而不依赖于它内部的值。它是一个实现细节,但它在Python版本之间有所不同。在Python的更新版本中,hash()和id()之间的关系涉及一些随机化。

但是它实际上如何散列呢?

虽然细节非常复杂并且可能涉及一些高级数学,但是元组对象的散列函数的实现是用C语言编写的,可以在这里看到(参见static Py_hash_t tuplehash(PyTupleObject *v)

计算涉及使用每个元组元素的哈希对一​​个常量进行异或运算。负责元素散列的行是这样的:

 y = PyObject_Hash(*p++);

所以对特定的数据结构来说,有一大波XOR操作加在数据结构的每个元素的来进行哈希。是否使用这些元素的内容取决于它们的特定散列函数。

编译自:
https://stackoverflow.com/que...

淡紫姑娘! 2022-09-18 15:32:01

dict 的 key 必须要是能够 hash
元组是一组值的序列。 其中的值可以是任意类型, 使用整数索引,

s = ([1,2],[2]) # 有list 存在不能够hash
print(type(s))
print(hash(s))
s2 = list(1,2)
print(hash(s2)) # 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文