Python 3.41一套

Python 3.41 A set

本问题已经有最佳答案,请猛点这里访问。

我有两个关于布景的问题。

1。所以当我读到集是无序的,但当我开始试验它们时,我发现实际上有某种排序的东西。

正如您所看到的,这个集合中没有什么特别的:

1
2
3
>>> v_set ={88,11,1,33,21,3,7,55,37,8}
>>> v_set
{33, 1, 3, 37, 7, 8, 11, 21, 55, 88}

但这个不同:

1
2
3
>>> g_set={7,5,11,1,4,13,55,12,2,3,6,20,9,10}
>>> g_set
{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 20, 55}

我想,这是因为这次我写下了更多更接近的数字,并且开始有意义地设置这些数字的升序…?

2。第二个问题是关于pop()。我读到没有办法控制用pop()方法删除哪个值,它是完全任意的。打赌,当我使用pop()方法时,它总是(我从来没有看到不同的情况)从左侧集合获取第一个项目。

如你所见:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> v_set
{33, 1, 3, 37, 7, 8, 11, 21, 55, 88}
>>> v_set.pop()
33
>>> v_set.pop()
1
>>> v_set.pop()
3
>>> v_set.pop()
37
>>> v_set.pop()
7
>>> v_set.pop()
8
>>> v_set.pop()
11
>>> v_set.pop()
21
>>> v_set.pop()
55

那么它真的是完全武断的吗?


注意元素的顺序也取决于插入的顺序。当发生碰撞时,您很容易看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In [4]: class Bad:
   ...:     def __init__(self, val, hash_val):
   ...:         self.val = val
   ...:         self.hash_val = hash_val
   ...:     def __str__(self):
   ...:         return 'Bad({0.val}, {0.hash_val})'.format(self)
   ...:     __repr__ = __str__
   ...:     def __eq__(self, other):
   ...:         return self.val == other.val
   ...:     def __hash__(self):
   ...:         return self.hash_val

In [5]: b1 = Bad(1, 1)
   ...: b2 = Bad(2, 1)
   ...: b3 = Bad(3, 2)

In [6]: {b1, b2, b3}
Out[6]: {Bad(2, 1), Bad(3, 2), Bad(1, 1)}

In [7]: {b2, b1, b3}
Out[7]: {Bad(1, 1), Bad(3, 2), Bad(2, 1)}

Out[6]中可以看到,第一个元素是Bad(2, 1),最后一个元素是Bad(1, 1),而在Out[7]中,第一个元素是Bad(1, 1),最后一个元素是Bad(2, 1)

如果没有碰撞:

1
2
3
4
5
6
7
8
9
In [8]: b1 = Bad(1, 1)
   ...: b2 = Bad(2, 2)
   ...: b3 = Bad(3, 3)

In [9]: {b1, b2, b3}
Out[9]: {Bad(1, 1), Bad(2, 2), Bad(3, 3)}

In [10]: {b2, b1, b3}
Out[10]: {Bad(1, 1), Bad(2, 2), Bad(3, 3)}

注意订单没有改变。(好吧,散列使用的是一些n的模,因此即使散列不同,也可能会发生冲突,这取决于底层表的大小)。

换句话说,这些值不足以确定set元素的顺序,即使您知道它们是如何实现的。您还必须知道插入的顺序。

一般来说,sets在一个解释器运行中确实有一个定义良好的顺序(由于python3.3+中的随机化),但是使用哪个顺序取决于执行的插入(值和执行顺序),并且是任意的,即在python3.5中,它们可以不经通知而更改顺序,因此您不能依赖iT

他们可以真正随机化输出,但这只会增加开销,没有任何好处。


它不是完全武断的。但这并不重要。

我们称集合为无序的,因为作为该代码的用户或客户端,您不能依赖于特定的顺序。但是,根据集合实现的详细信息,可能有一些顺序。

pop()相同。您使用的Python的特定实现很可能具有逻辑,这将导致明显的确定性结果。但是,您的代码可能与使用不同实现的Python解释器一起使用。A random element是您从实现中获得的唯一保证。

总之,文档为您提供了一组保证,任何兼容的Python实现都将遵循这些保证。您观察到的其他影响是实现细节,可能随时发生变化。


是的,根据定义,排序是任意的。即使项目按排序顺序存储,它仍然是任意的。""任意"意味着Python不承诺以任何特定的方式订购数据。因为内存是线性的,所以它必须使用某种顺序,但是您永远不应该依赖于这种顺序,因为它可能会在未经通知的情况下被更改。(实际上,在最新版本的python中,set中的项目顺序是部分随机的。)

第二个示例显示打印顺序与弹出顺序相同。这是有道理的:repr按照存储在内存中的顺序对项目进行遍历,pop显然按照相同的顺序返回第一个项目。同样,您不能依赖于这一点:这是一个实现细节,如果Python开发人员找到一种更快的方法来执行pop,他们可以自由地破坏任何依赖set排序的代码。

如果您想知道这是如何工作的,请阅读哈希表。