注意此处描述的实现适用于CPython。我认为一般描述适用于所有现代版本的CPython(在本次更新时最多为3.6),但是从CPython3.6开始,还有一个实现细节,实际上保留了迭代的插入顺序。数据结构由pypy的人描述(
this blog post开始在CPython人之前使用)。原来的想法(至少对于python生态系统)
is archived on the python-dev mailing list。
你应该看这video(虽然它是CPython具体和关于字典 – 但我认为它也适用于集)。
基本上,python对元素进行散列,并获取最后N位(其中N由集合的大小确定),并使用那些位作为数组索引将对象放置在内存中。然后按照它们在存储器中存在的顺序产生对象。当然,当你需要解决哈希之间的冲突时,图片会变得更加复杂,但这是它的要点。
还要注意,打印输出的顺序由您放置它们的顺序决定(由于碰撞)。因此,如果您重新排序传递给set_2的列表,则如果存在键冲突,您可能会得到不同的顺序。
例如:
list1 = [8,16,24]
set(list1) #set([8, 16, 24])
list2 = [24,16,8]
set(list2) #set([24, 16, 8])
注意,在这些集合中保存的顺序是“巧合”并且与冲突解决有关(事实上我不知道什么)。重点是,散列(8),散列(16)和散列(24)的最后3位是相同的。因为它们是相同的,冲突解决接管并将元素放置在“备份”存储器位置而不是第一(最佳)选择中,因此8是占据一个位置还是16是由哪一个首先到达对方并且取“最佳座位”。
如果我们用1,2和3重复这个例子,无论输入列表中有什么顺序,都会得到一个一致的顺序:
list1 = [1,2,3]
set(list1) # set([1, 2, 3])
list2 = [3,2,1]
set(list2) # set([1, 2, 3])
因为散列(1),散列(2)和散列(3)的最后3位是唯一的。