共计 1514 个字符,预计需要花费 4 分钟才能阅读完成。
Python 内置了一个不可变 (immutable) 汇合对象:frozenset,顾名思义,这种汇合对象,一旦创立了就不能批改。
概念
上面的代码演示了可变和不可变汇合应用上的差异:
l = [1, 2, 3, 4]
new_set = set(l)
new_set.remove(1)
print (new_set)
fset = frozenset(l)
fset.remove(1)
如果运行这段代码,它的输入会是:
{2, 3, 4}
Traceback (most recent call last):
File "main.py", line 9, in <module>
fset.remove(1)
AttributeError: 'frozenset' object has no attribute 'remove'
能够看到 frozenset 实例基本没有 remove 办法,当然它也没有 append 办法,不能用 index 给元素赋值。
作用
除了不可批改以外,frozenset 所有的用法都和一般 set 一样。
这样一个不可变的汇合,有什么用呢?对性能应该没有什么帮忙,已经有文章指出它比一般的 set 还稍慢一点,次要价值还是体现在可读性和安全性上:
- 显式地申明了该汇合是不可更改的,给使用者明确提醒,一旦误用了,运行时也会敌对报错;
- 如果想把这个汇合,放到另一个 set 外面,或者作为 dict 的 key,那它就必须是不可更改的
这第二点,次要是因为 dict 的 key 要求是惟一的(或者 set 内的元素也是惟一的),要是传一个对象,python 会尝试调用它的 hash 办法生个哈希串,作为判断是否惟一的根据,而如果这个对象竟然是可变的,它的内容就会影响哈希串的值。所以在 python 里,可变对象基本就没有内置 hash 办法,防止它被当做 key。
比方上面这段代码:
fset = frozenset({4, 5})
s = {1, 2, fset}
print (s)
s = {1, 2, {4, 5}}
首先定义了一个 frozenset,把它插入一个一般 set;最初一句试图在一般 set 外面,初始化另外一个一般 set。输入后果如下:
{frozenset({4, 5}), 1, 2}
Traceback (most recent call last):
File "main.py", line 27, in <module>
s = {1, 2, {4, 5}}
TypeError: unhashable type: 'set'
可见一般 set 是不能作为另外一个 set 的元素的,但 frozenset 就能够。‘
为什么 Java 没有这个限度
Java 程序员可能会感觉奇怪,在 hashset 里放一个可变对象,不是很失常么?凭什么不行啊,咱们 Java 常常这样写:
HashSet<ArrayList<String>> masterCollection = new HashSet<ArrayList<String>>();
ArrayList<String> a = new ArrayList<String>();
masterCollection.add(a);
a.add("Hello, World");
for(ArrayList<String> list : masterCollection) {// do something to list}
齐全不会报错么。
So,难道 Java 的实现机制和 Python 不一样?
答案是并没有,Java 只是“容忍”了可能产生的谬误——这个帖子解释得很分明。简略来说:
Java 和 Python 都是用一个对象的 hash 值作为 key 的,相比 Python 更器重数据一致性,Java 更看中灵活性,所以如果咱们真的在 HashSet 里放了一个可变对象,并且预先扭转了它的值,那么就会发现,HashSet 汇合,曾经检测不到它的存在了!