为什么下面的代码在Python中会出现意外的行为?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?
>>> 257 is 257
True           # Yet the literal numbers compare properly

我使用的是Python 2.5.2。尝试一些不同版本的Python,似乎Python 2.3.3在99到100之间显示了上述行为。

基于以上,我可以假设Python是内部实现的,这样“小”整数与大整数以不同的方式存储,并且is操作符可以区分两者。为什么会有漏洞的抽象?有什么更好的方法来比较两个任意物体来判断它们是否相同当我事先不知道它们是否是数字时?


当前回答

这取决于你要看的是两个物体是否相等,还是同一个物体。

是检查它们是否是相同的对象,而不仅仅是相等。较小的int可能指向相同的内存位置以提高空间效率

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

您应该使用==来比较任意对象的相等性。你可以用__eq__和__ne__属性来指定行为。

其他回答

对于不可变值对象,比如int、字符串或datetimes,对象标识并不是特别有用。最好还是考虑一下平等。标识本质上是值对象的实现细节——因为它们是不可变的,所以对同一个对象的多次引用和对多个对象的多次引用之间没有有效的区别。

Python 3.8新增功能:Python行为的变化:

编译器现在在标识检查(is和 Is not)用于某些类型的字面量(例如字符串,int)。 在CPython中,这些通常可以意外地工作,但不能保证 该警告建议用户使用相等性测试(== 和!=)代替。

看看这个:

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

以下是我在“普通整数对象”的文档中找到的:

当前的实现为-5到256之间的所有整数保留了一个整数对象数组。当你在这个范围内创建一个int型时,你实际上只是得到了一个对现有对象的引用。

Is是恒等运算符(功能类似于id(a) == id(b));只是两个相等的数不一定是同一个物体。出于性能考虑,有些小整数会被记忆,所以它们往往是相同的(这可以做到,因为它们是不可变的)。

另一方面,PHP的===运算符被描述为检查相等性和type: x == y和type(x) == type(y),正如Paulo Freitas的注释所述。对于常见的数字,这就足够了,但与之不同的是,类以一种荒谬的方式定义__eq__:

class Unequal:
    def __eq__(self, other):
        return False

PHP显然也允许“内置”类(我指的是在C级实现的,而不是在PHP中)。稍微不那么荒谬的用法可能是timer对象,它每次作为数字使用时都有不同的值。这就是为什么你想要模拟Visual Basic的Now而不是用time。time()来显示它是一个求值,我不知道。

Greg Hewgill (OP)做了一个澄清性的评论:“我的目标是比较对象的同一性,而不是价值相等。除了数字,我想把对象的同一性视为价值相等。”

这将有另一个答案,因为我们必须将事物分类为数字,以选择是否与==或is进行比较。CPython定义了数字协议,包括PyNumber_Check,但这不能从Python本身访问。

我们可以尝试对已知的所有数字类型使用isinstance,但这不可避免地是不完整的。types模块包含一个StringTypes列表,但没有NumberTypes。从Python 2.6开始,内置的数字类有一个基类numbers。数字,但它有同样的问题:

import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)

顺便说一下,NumPy将生成低数字的单独实例。

其实我不知道这个问题的答案。我认为理论上可以使用ctypes来调用PyNumber_Check,但即使是这个函数也存在争议,而且它肯定是不可移植的。我们现在只需要对测试的内容不那么挑剔。

最后,这个问题源于Python最初没有一个类型树的谓词,如Scheme的数字?,或者Haskell的类型类Num. is检查对象的身份,而不是值是否相等。PHP也有丰富多彩的历史,在PHP5中===显然只对对象起作用,而PHP4中没有。这就是跨语言(包括一种语言的不同版本)的成长之痛。

这取决于你要看的是两个物体是否相等,还是同一个物体。

是检查它们是否是相同的对象,而不仅仅是相等。较小的int可能指向相同的内存位置以提高空间效率

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

您应该使用==来比较任意对象的相等性。你可以用__eq__和__ne__属性来指定行为。