首页 > 解决方案 > 将集合转换为列表时,是什么决定了项目顺序?

问题描述

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

所以我知道setPython 中的 a 是一个无序集合,而 a 是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}

这让我想到了我的问题。如果将集合转换为列表,则必须确定集合中的每个项目在新列表中的位置。但是,在执行集合时确定项目打印顺序的逻辑似乎并不相同,这是我天真的想法。虽然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),但首先要找出错误需要一些时间。

因此,对于这个问题,我试图了解正在发生的事情,而不是寻找解决方案。

标签: pythonpython-3.xlistsortingset

解决方案


我在 Python 上3.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是如何表示的,这是根据初始评论的一个实现细节。


推荐阅读