我在 Pluralsight 上看到过这道测试题:
鉴于这些集合:
x = {'a', 'b', 'c', 'd'} y = {'c', 'e', 'f'} z = {'a', 'g', 'h', 'i'}
的价值是多少x | y ^ z?
x | y ^ z
预期答案是:
{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'}
合并集合(自动丢弃重复项),并按从低到高的顺序排列。
我的问题是:
Ubuntu 18.04 上 Python 3.7.5 的结果:
{'c', 'h', 'f', 'd', 'b', 'i', 'g', 'a', 'e'}
Ubuntu 18.04 上 Python 2.17.17rc1 的结果:
set(['a', 'c', 'b', 'e', 'd', 'g', 'f', 'i', 'h'])
Windows 10 上 Python 3.7.2 的结果:
{'a', 'd', 'h', 'f', 'b', 'g', 'e', 'c', 'i'}
下面是我为此使用的相同代码的 repl: https://repl.it/repls/RudeMoralWorkplace
我想了解这些表达式背后发生了什么,这样我就可以揭穿为什么我会得到不同的结果。
这个问题涉及到集合操作和元素的顺序,在不同版本的 Python 中,集合的顺序处理方式不同。让我们一步步解析这个问题。
表达式 x | y ^ z 涉及两个集合操作:
|
^
y
z
因此,x | y ^ z 实际上表示:
y ^ z
x
因此,整个表达式的逻辑是:
x | (y ^ z)
关键点是:集合的元素顺序在不同的 Python 版本中可能会有所不同,尤其是 Python 3.7 之前的版本。让我们详细说明。
Python 3.7 及之后版本:从 Python 3.7 开始,集合的元素顺序会被保留,即集合的元素将按照它们插入的顺序排列。因此,Python 3.7 及之后版本会保证你在 x | y ^ z 操作中看到的顺序与插入顺序一致。
Python 3.6 及之前版本:在这些版本中,集合是无序的,因此元素的顺序是不可预测的。这意味着你每次运行代码时,得到的集合元素的顺序可能不同。
在 Python 3.7 及之后版本中,集合的插入顺序会被保留,但是具体的顺序可能受到操作顺序的影响。即便是插入顺序被保留,像对称差(^)和并集(|)操作可能会重新排列这些元素的顺序。因此,你看到的这个顺序是 Python 3.7 在计算时的插入顺序。
在 Python 2.x 中,集合的顺序是不可预测的,因为集合没有保证插入顺序。这个输出就是随机的,可能会在不同的 Python 2.x 环境中有所不同,甚至同一个环境中多次运行结果也可能不同。
尽管这是 Python 3.7,但它的顺序与 Python 3.7.5 上的结果有所不同。这种差异可能与平台、内存管理、或者 Python 实现细节有关。即使是同一版本的 Python,因不同操作系统或硬件环境的不同,集合的顺序可能会有所变化,尤其是在对称差和并集操作涉及多个集合时。
对称差 (^):计算 y ^ z 结果是那些只存在于 y 或 z 中,但不同时出现在这两个集合中的元素。在本例中,y ^ z 会给出 {'e', 'f', 'a', 'g', 'h', 'i'}。
{'e', 'f', 'a', 'g', 'h', 'i'}
并集 (|):然后,你将集合 x = {'a', 'b', 'c', 'd'} 与 y ^ z = {'e', 'f', 'a', 'g', 'h', 'i'} 进行并集,得到所有的元素,不重复。并集的结果是: python {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'}
x = {'a', 'b', 'c', 'd'}
y ^ z = {'e', 'f', 'a', 'g', 'h', 'i'}
python {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'}
元素顺序:在 Python 3.7 及之后版本,集合保留元素插入顺序,但对称差和并集操作可能会在不同的 Python 实现中影响插入顺序,尤其是在不同的操作系统或 Python 编译器上。由于 y ^ z 和 x 中的元素会在执行时插入到结果集合中,具体的顺序可能会有所不同。
如果你希望得到确定的结果顺序,可以显式地对集合进行排序:
sorted(x | (y ^ z))
这将确保无论在哪个 Python 版本上运行,都会得到相同的排序结果。