单元测试模块时遇到了一个令人困惑的问题。该模块实际上是铸造值,我想比较这些值。
与==
和is
相比有所不同(部分地,我要注意区别)
>>> 0.0 is 0.0
True # as expected
>>> float(0.0) is 0.0
True # as expected
正如预期到现在为止,但这是我的“问题”:
>>> float(0) is 0.0
False
>>> float(0) is float(0)
False
为什么呢?至少最后一个对我来说真的很困惑。 float(0)
和float(0.0)
的内部表示应该相等。与==
的比较正如预期的那样。
答案 0 :(得分:21)
这与is
的工作方式有关。它检查引用而不是值。如果将任一参数分配给同一对象,则返回True
。
在这种情况下,它们是不同的实例; float(0)
和float(0)
具有相同的值==
,但就Python而言,它们是不同的实体。 CPython实现还将整数缓存为此范围内的单例对象 - > [x | x∈ℤ∧-5≤x≤256] :
>>> 0.0 is 0.0
True
>>> float(0) is float(0) # Not the same reference, unique instances.
False
在这个例子中,我们可以演示整数缓存原理:
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
现在,如果将浮点值传递给float()
,则只返回浮点文字(短路),就像使用相同的引用一样,因为没有需要从现有的float中实例化一个新的float:
>>> 0.0 is 0.0
True
>>> float(0.0) is float(0.0)
True
这可以通过使用int()
进一步证明:
>>> int(256.0) is int(256.0) # Same reference, cached.
True
>>> int(257.0) is int(257.0) # Different references are returned, not cached.
False
>>> 257 is 257 # Same reference.
True
>>> 257.0 is 257.0 # Same reference. As @Martijn Pieters pointed out.
True
但是,is
的结果也取决于它的执行范围(超出此问题/解释的范围),请参阅用户: @ Jim 对code objects的精彩解释。即使是python的doc也包含有关此行为的部分:
<强> [7] 强> 由于自动垃圾收集,空闲列表和描述符的动态特性,您可能会注意到
is
运算符的某些使用中看似异常的行为,如涉及实例方法或常量之间的比较。查看他们的文档以获取更多信息。
答案 1 :(得分:9)
如果向float
提供float()
对象,则 CPython *只返回它而不创建新对象。
这可以在PyNumber_Float
(最终从float_new
调用)中看到,其中传入的对象o
使用PyFloat_CheckExact
进行检查;如果True
,它只会增加其引用计数并返回它:
if (PyFloat_CheckExact(o)) {
Py_INCREF(o);
return o;
}
因此,对象的id
保持不变。所以表达式
>>> float(0.0) is float(0.0)
缩减为:
>>> 0.0 is 0.0
但为什么这等于True
?好吧,CPython
有一些小优化。
在这种情况下,它在命令中两次出现0.0
时使用相同的对象,因为它们是the same code
object的一部分(简短免责声明:它们位于同一逻辑行上);所以is
测试会成功。
如果您在单独的行中执行float(0.0)
(或由;
分隔)并且然后检查身份,则可以进一步证实这一点:
a = float(0.0); b = float(0.0) # Python compiles these separately
a is b # False
另一方面,如果提供了int
(或str
),CPython将从中创建一个 new float
对象并返回该对象。为此,它分别使用PyFloat_FromDouble
和PyFloat_FromString
。
效果是返回的对象在id
s(用于检查is
的身份)中有所不同:
# Python uses the same object representing 0 to the calls to float
# but float returns new float objects when supplied with ints
# Thereby, the result will be False
float(0) is float(0)
*注意:前面提到的所有行为都适用于C
中CPython
的python实现。其他实现可能表现出不同的行为简而言之,不依赖它。