为什么浮点字典键可以覆盖具有相同值的整型键?

2024-10-08 19:28:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在通过http://www.mypythonquiz.com进行工作,question #45要求输出以下代码:

confusion = {}
confusion[1] = 1
confusion['1'] = 2
confusion[1.0] = 4

sum = 0
for k in confusion:
    sum += confusion[k]

print sum

输出为6,因为键1.0替换了1。这对我来说有点危险,这是有用的语言功能吗


Tags: 代码in功能com语言httpforwww
3条回答

首先:hash函数的文档中明确记录了该行为:

hash(object)

Return the hash value of the object (if it has one). Hash values are integers. They are used to quickly compare dictionary keys during a dictionary lookup. Numeric values that compare equal have the same hash value (even if they are of different types, as is the case for 1 and 1.0).

其次,在^{}的文档中指出了散列的限制

object.__hash__(self)

Called by built-in function hash() and for operations on members of hashed collections including set, frozenset, and dict. __hash__() should return an integer. The only required property is that objects which compare equal have the same hash value;

这不是python独有的。Java也有同样的警告:如果您实现了hashCode,那么为了让事情正常运行,您必须以这样一种方式实现它:x.equals(y)意味着x.hashCode() == y.hashCode()

因此,python决定1.0 == 1保持不变,因此它被迫hash提供一个实现,以便hash(1.0) == hash(1)。副作用是1.01的行为与dict键的行为完全相同,因此这种行为是正确的

换句话说,行为本身不必以任何方式使用或有用这是必要的。如果没有这种行为,您可能会意外地覆盖不同的密钥

如果我们有1.0 == 1但是hash(1.0) != hash(1),我们仍然可以有一个碰撞。如果1.01发生冲突,dict将使用相等来确定它们是否是同一个键,kaboom即使您希望它们不同,也会覆盖该值

避免这种情况的唯一方法是使用1.0 != 1,这样dict即使在发生冲突的情况下也能够区分它们。但是,人们认为拥有1.0 == 1比避免你看到的行为更重要,因为你实际上从来没有使用floatint作为字典键

由于python试图通过在需要时自动转换数字(例如1/2 -> 0.5)来隐藏数字之间的区别,因此即使在这种情况下也可以反映这种行为。它与python的其他部分更加一致


这种行为会出现在任何实现中,其中键的匹配至少部分地(如在哈希映射中)基于比较

例如,如果一个dict是使用红黑树或其他类型的平衡BST实现的,当查询键1.0时,与其他键的比较将返回与1相同的结果,因此它们仍然以相同的方式工作

散列映射需要更加小心,因为用于查找密钥项的是散列值,只有在之后才进行比较。因此,违反上述规则意味着您将引入一个很难发现的错误,因为有时dict可能会像您预期的那样工作,而在其他时候,当大小发生变化时,它将开始表现不正确


注意,将有一种方法来解决这个问题:为字典中插入的每种类型都有一个单独的hash-map/BST。通过这种方式,不同类型的对象之间不会发生任何冲突,并且当参数具有不同类型时,==的比较方式也无关紧要

然而,这将使实现复杂化,这可能是低效的,因为哈希映射必须保留相当多的空闲位置才能有O(1)个访问时间。如果它们变得太满,性能就会下降。拥有多个哈希映射意味着浪费更多空间,而且在开始实际查找密钥之前,您需要首先选择要查看的哈希映射

如果使用BST,则必须首先查找类型,然后执行第二次查找。因此,如果您要使用许多类型,那么最终的工作量将是原来的两倍(并且查找将需要O(logn)而不是O(1))

您应该考虑到^ {CD1>}的目的是根据逻辑数值存储数据,而不是如何表示它。p>

intfloat之间的区别实际上只是一个实现细节,而不是概念性的。理想情况下,唯一的数字类型应该是具有无限精度甚至亚单位的任意精度数字。。。然而,如果不遇到麻烦,这是很难实现的。。。但这可能是Python未来唯一的数字类型

因此,由于技术原因,Python使用不同的类型时,会试图隐藏这些实现细节和int->float转换是自动的

如果在Python程序中,当x是一个值为1的floatif x == 1: ...不会被接受,那就更令人惊讶了

请注意,在Python3中,1/2的值是0.5(两个整数的除法),类型long和非unicode字符串也被删除,同时尝试隐藏实现细节

在python中:

1==1.0
True

这是因为隐式铸造

然而:

1 is 1.0
False

我可以理解为什么在floatint之间进行自动强制转换很方便,将int强制转换为float相对安全,但是还有其他语言(例如go)不进行隐式强制转换

这实际上是一个语言设计的决定,而不仅仅是不同功能的品味问题

相关问题 更多 >

    热门问题