小能豆

这些集合运算是什么,为什么它们会给出不同的结果?

py

我在 Pluralsight 上看到过这道测试题:

鉴于这些集合:

x = {'a', 'b', 'c', 'd'}
y = {'c', 'e', 'f'}
z = {'a', 'g', 'h', 'i'}

的价值是多少x | y ^ z

预期答案是:

{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'}

合并集合(自动丢弃重复项),并按从低到高的顺序排列。

我的问题是:

  • 这个表情叫什么?
  • 为什么我从 3 个不同的 Python 版本获得 3 个不同的结果?

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

我想了解这些表达式背后发生了什么,这样我就可以揭穿为什么我会得到不同的结果。


阅读 6

收藏
2024-11-14

共1个答案

小能豆

这个问题涉及到集合操作和元素的顺序,在不同版本的 Python 中,集合的顺序处理方式不同。让我们一步步解析这个问题。

1. 这个表达式叫什么?

表达式 x | y ^ z 涉及两个集合操作:

  • | 是集合的 并集 操作符,表示将两个集合的元素合并,自动去重。
  • ^ 是集合的 对称差 操作符,表示返回在 yz 中但不同时在两个集合中的元素。

因此,x | y ^ z 实际上表示:

  1. 计算 y ^ z(即对称差),得到那些只存在于 yz 中,但不同时在两个集合中的元素。
  2. 然后,计算 xy ^ z 的并集,即把 x 中的元素和 y ^ z 中的元素合并。

因此,整个表达式的逻辑是:

x | (y ^ z)

2. 为什么在 3 个不同的 Python 版本中得到 3 个不同的结果?

关键点是:集合的元素顺序在不同的 Python 版本中可能会有所不同,尤其是 Python 3.7 之前的版本。让我们详细说明。

集合在不同 Python 版本中的顺序

  • Python 3.7 及之后版本:从 Python 3.7 开始,集合的元素顺序会被保留,即集合的元素将按照它们插入的顺序排列。因此,Python 3.7 及之后版本会保证你在 x | y ^ z 操作中看到的顺序与插入顺序一致。

  • Python 3.6 及之前版本:在这些版本中,集合是无序的,因此元素的顺序是不可预测的。这意味着你每次运行代码时,得到的集合元素的顺序可能不同。

让我们逐步分析每个 Python 版本的输出:

Python 3.7.5 (Ubuntu 18.04)

{'c', 'h', 'f', 'd', 'b', 'i', 'g', 'a', 'e'}

在 Python 3.7 及之后版本中,集合的插入顺序会被保留,但是具体的顺序可能受到操作顺序的影响。即便是插入顺序被保留,像对称差(^)和并集(|)操作可能会重新排列这些元素的顺序。因此,你看到的这个顺序是 Python 3.7 在计算时的插入顺序。

Python 2.7.17rc1 (Ubuntu 18.04)

set(['a', 'c', 'b', 'e', 'd', 'g', 'f', 'i', 'h'])

在 Python 2.x 中,集合的顺序是不可预测的,因为集合没有保证插入顺序。这个输出就是随机的,可能会在不同的 Python 2.x 环境中有所不同,甚至同一个环境中多次运行结果也可能不同。

Python 3.7.2 (Windows 10)

{'a', 'd', 'h', 'f', 'b', 'g', 'e', 'c', 'i'}

尽管这是 Python 3.7,但它的顺序与 Python 3.7.5 上的结果有所不同。这种差异可能与平台、内存管理、或者 Python 实现细节有关。即使是同一版本的 Python,因不同操作系统或硬件环境的不同,集合的顺序可能会有所变化,尤其是在对称差和并集操作涉及多个集合时。

3. 背后发生了什么?

  • 对称差 (^):计算 y ^ z 结果是那些只存在于 yz 中,但不同时出现在这两个集合中的元素。在本例中,y ^ z 会给出 {'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'}

  • 元素顺序:在 Python 3.7 及之后版本,集合保留元素插入顺序,但对称差和并集操作可能会在不同的 Python 实现中影响插入顺序,尤其是在不同的操作系统或 Python 编译器上。由于 y ^ zx 中的元素会在执行时插入到结果集合中,具体的顺序可能会有所不同。

4. 总结

  • 表达式的名称:这个表达式包含 对称差(^并集(| 操作。
  • 不同结果的原因:集合的顺序在 Python 3.7 之前是不可预测的,因此你会看到不同的结果顺序。在 Python 3.7 及之后版本,插入顺序得到了保证,但由于不同的实现或平台,仍然可能会看到一些微小的差异。

如果你希望得到确定的结果顺序,可以显式地对集合进行排序:

sorted(x | (y ^ z))

这将确保无论在哪个 Python 版本上运行,都会得到相同的排序结果。

2024-11-14