将集合转换为列表时,是什么决定项目顺序



我知道有几个类似的问题,但我还没有找到一个能说明我想知道什么的问题。如果这是重复的,请指出。

所以我知道Python中的set是一个无序的集合,而list是可以排序的。我想知道的是,当列表从集合转换时,是什么决定了列表中的项目是如何排序的。

即使一个集合在"技术上"没有排序(我想这只是意味着你不能像处理序列类型那样与它交互(,但在打印一个集合时,仍然有一个顺序,例如,必须有一个项目先打印、第二打印、第三打印等等。这种逻辑需要存在。但它甚至更进一步。例如,如果您声明两个处于"加扰"状态的集合,其中包含可以排序的项,则不仅在执行它们时对它们的表示进行排序,而且两个"加扰的"集合的并集还返回一个"排序的"集:

a = {2, 3, 1}
a
# >>> {1, 2, 3}
b = {7, 4, 5}
b
# >>> {4, 5, 7}
a|b  
# >>> {1, 2, 3, 4, 5, 7} 
b|a
# >>> {1, 2, 3, 4, 5, 7}

此外,当您将新项目add添加到集合并打印集合时,新项目会出现在正确的位置,即如果对集合进行排序,它应该出现的位置:

b.add(6)
b
# >>> {4, 5, 6, 7}

这就引出了我的问题。如果将集合转换为列表,则必须确定集合中的每个项目在新列表中的位置。但从外观上看,NOT与决定执行集合时项目打印顺序的逻辑相同,这也是我天真地认为的。虽然list(a)list(b)甚至list(a|b)都返回了按照集合表示方式排序的列表,但对于以下集合(以及它的所有排列(,出于某种原因,情况并非如此:

list(a), list(b), list(a|b)
# >>> ([1, 2, 3], [4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7])
c = {3, 4, 9}  # or {3, 9, 4}, {4, 3, 9} and so on...
c
# >>> {3, 4, 9}
list(c)  
# >>> [9, 3, 4]

为什么?为什么当集合转换为列表时,确定集合表示的排序方式的逻辑与确定集合中每个项的位置的逻辑不同?

我又尝试了几个具有不同值的集合,对我来说,当集合的表示顺序和集合的列表顺序相同时,这似乎是完全随机的:

# for this set of numbers, the logic is different
d = {3, 4, 11}
d
# >>> {3, 4, 11}
list(d)  
# >>> [11, 3, 4]
# in this case, permutations also result in different sorting of the list
d = {11, 4, 3}
d
# >>> {3, 4, 11}
list(d)  
# >>> [3, 11, 4]
# for this set, the logic appears to be the same again
e = {3, 4, 13}  # or any of its permutations
e
# >>> {3, 4, 13}
list(e)
# >>> [3, 4, 13]

确定列表顺序和调用print(set)的逻辑似乎是一样的:

list(d)  
# >>> [3, 11, 4]
print(d)
# >>> {3, 11, 4}

所以我想,只要对集合做操作,就会应用不同的排序逻辑。当然,除非你创建了联盟:

print(c, d, c|d, list(c|d))
# >>> {9, 3, 4} {3, 11, 4} {3, 4, 9, 11} [3, 4, 9, 11]
f = {3, 4, 9, 11}
f
# >>> {3, 4, 9, 11}
list(f)
# >>> [11, 9, 3, 4]

如果你想知道这个用例:正如我所说,我天真地认为在将集合转换为列表时排序会保持不变,但事实并非如此。错误的排序导致运行代码时出错。幸运的是,使用sorted(set)而不是list(set)很容易修复,但一开始就花了一些时间来找出错误。

因此,对于这个问题,我试图了解发生了什么,而不是寻找解决方案。

我在Python3.7.4.上,我所有的list(set)顺序都与repr(set)顺序一致。以下是对10000个样本的快速测试(代码(:

import random
import pandas as pd
# create a function to generate random set of 0-999 with len of 3 - 20
f = lambda: set(random.randrange(1000) for i in range(random.randrange(3, 21)))
# create a DataFrame of 10000 rows with random sets
df = pd.DataFrame({'sets': [f() for i in range(10000)]})
# Create a column of repr(set) and retrieve the order in str
df['reprs'] = df['sets'].apply(repr).str.strip('{}')
# Create a column of list(set) and retrieve the order in str
df['lists'] = df['sets'].apply(list).astype(str).str.strip('[]')
# Create a comparison column
df['match'] = df['reprs'].eq(df['lists'])
# Take a look of the reprs and lists...
print(df[['reprs', 'lists']])
# Summarize
summary = df.groupby('match')['sets'].count()
print(summary)

结果:

match
True    10000
Name: sets, dtype: int64

所以我想,如果你想关注set的表示方式,这是每个初始注释的实现细节。

我相信OP观察到的是内部值的影响。

考虑:

>>> list({*range(2,99), True, False})
[False, True, 2, 3, 4, 5, 6, 7, 8, 9, ...]

但是:

>>> list({*range(2002,2099), True, False})
[2048, 2049, ..., True, False, ..., 2047] 

然而,这并不能完全解释OP自己的观察结果:

>>> list({3,4,9, True, False})
[False, True, 3, 4, 9]
>>> list({3,4,9})
[9, 3, 4]

最新更新