集合中同哈希值元素共存?SymPy与Python数值混合疑问
问题:SymPy Float与Python原生数值混合存入集合的去重异常
我在查阅SymPy 1.13.0发布说明时注意到,官方不建议将SymPy的Basic对象与Python原生float、int等数值类型混合存入同一set或dict。测试发现:字符串中同值对象哈希相同,集合能正常去重;但将Python原生2.0、SymPy的Float(2.0)、Float(2.0, precision=80)存入集合时,三者哈希均为2,却出现两个元素共存的情况,对此存在疑问。
字符串测试
a, b = "a", "b" c1 = "This is a test" c2 = "This is a test" t = [a, b, c1, c2] print("ids:", ", ".join([str(id(e)) for e in t])) print("hashes:", ", ".join([str(hash(e)) for e in t])) s = set(t) print(s) # 输出: # ids: 11791568, 11792624, 135187170594160, 135187170598448 # hashes: -8635426522778860779, 3774980000107278733, 3487163462126586929, 3487163462126586929 # {'This is a test', 'a', 'b'}
SymPy数值测试
from sympy import * n1 = 2.0 n2 = Float(2.0) n3 = Float(2.0, precision=80) t = [n1, n2, n3] print("ids:", ", ".join([str(id(e)) for e in t])) print("hashes:", ", ".join([str(hash(e)) for e in t])) s = set(t) print(s) # 输出: # ids: 135913432385776, 135912654307472, 135912654307632 # hashes: 2, 2, 2 # {2.0, 2.0000000000000000000000}
原因解析
Python集合的去重逻辑分两步:
- 先比较元素的哈希值,哈希值不同则直接视为不同元素;
- 哈希值相同时,再通过
==运算符判断元素是否相等,只有==返回True才会被视为同一元素去重。
- 字符串场景:同内容的字符串哈希值相同,且
==会直接比较内容返回True,因此集合能正常去重。 - SymPy数值场景:
- Python原生
2.0与SymPyFloat(2.0)的哈希值均为2,且2.0 == Float(2.0)返回True,因此两者会被集合去重,只保留一个。 - 虽然
Float(2.0, precision=80)的哈希值也为2,但它与2.0(或Float(2.0))的==比较返回False(SymPy高精度Float与原生float/低精度Float的相等判断会考虑精度或类型差异),因此会被集合视为不同元素,最终与前者共存。
- Python原生
SymPy官方不建议混合存入的核心原因就是:不同类型/精度的数值对象即使数值等价,其==判断逻辑可能不符合直觉,加上哈希值可能巧合碰撞,会导致集合或字典的行为异常,无法保证预期的去重或键值映射效果。
内容的提问来源于stack exchange,提问作者Davide_sd
相关产品推荐
相关产品推荐

