Python中list转换为set时元素顺序受对象类型影响的原因是什么
问题描述
在将list对象转换为set对象时,如果列表包含整数或浮点数,元素的顺序看起来不会发生变化;但如果列表包含字符串,元素的顺序会被改变。测试代码如下:
def list_set_order(x): print(f'x:{x}') print(f'set(x):{set(x)}') print(f'list(set(x)):{list(set(x))}') list_set_order([1, 2, 3, 4, 5]) list_set_order([1., 2., 3., 4., 5.]) list_set_order(['1', '2', '3', '4', '5'])
原因解释
- 首先明确核心规则:Python官方从未承诺
set会保留元素的插入顺序,你观察到的整数、浮点数场景下顺序不变只是特定CPython版本、特定数值范围下的巧合,绝非稳定特性,绝对不能依赖该现象编写业务逻辑。 - 小整数、等值浮点数看起来顺序不变的原因:CPython实现中,小范围整数(通常为-5到2^30-1区间)的哈希值等于其本身,而
1.0这类整值浮点数的哈希值和对应整数完全相等。set底层是哈希表结构,元素插入时会按哈希值 % 哈希表长度分配存储槽位,当你插入的是连续小整数/对应浮点数时,哈希值连续递增,分配的槽位刚好和插入顺序一致,遍历哈希表时按槽位顺序读取元素,就会出现和原列表顺序一致的假象。 - 字符串顺序改变的原因:字符串的哈希值计算逻辑和数值完全不同,哪怕是纯数字组成的字符串
'1',它的哈希值也不等于数值1。另外CPython默认会给字符串哈希添加随机盐(避免哈希碰撞攻击),不同字符串的哈希值是随机分散的,插入哈希表时分配的槽位和插入顺序没有关联,遍历输出的自然就和原列表顺序不一致。
补充说明
- 这个顺序一致的现象非常不稳定:只要插入非连续数值、数值超出小整数范围、更换Python版本或运行环境,顺序就可能发生变化。比如你测试
list_set_order([3,1,2]),转set后的输出顺序大概率是[1,2,3],和原插入顺序完全不同。 - 如果你需要对列表去重同时保留插入顺序,Python 3.7+可以直接用
list(dict.fromkeys(原列表))实现,不需要依赖set的不稳定特性。
内容的提问来源于stack exchange,提问作者Caglar Demir
相关产品推荐
相关产品推荐

