Python的“is”运算符对整数表现异常?
总之,让我强调一下:不要使用is来比较整数。
你不应该对这种行为有任何期望。
相反,使用==和!=分别比较相等和不相等。例如:
>>> a = 1000
>>> a == 1000 # Test integers like this,
True
>>> a != 5000 # or this!
True
>>> a is 1000 # Don't do this! - Don't use `is` to test integers!!
False
解释
要了解这一点,您需要了解以下内容。
首先,是做什么?它是一个比较运算符。从文档中可以看到:
操作符is和is not测试对象的同一性:x是y是真
当且仅当x和y是同一个对象。X不是y会得到
逆真值。
所以下面两个是等价的。
>>> a is b
>>> id(a) == id(b)
从文档中可以看到:
id
返回一个对象的“标识”。这是一个整数(或长
Integer),保证该对象的唯一性和常量
在它的生命周期内。两个生命周期不重叠的对象可能
具有相同的id()值。
请注意,在CPython (Python的参考实现)中,对象的id是内存中的位置,这是一个实现细节。Python的其他实现(如Jython或IronPython)很容易有不同的id实现。
那么用例是什么呢?PEP8描述:
与像None这样的单例对象的比较应该总是使用is或
不是,不是相等运算符。
这个问题
你问并陈述以下问题(带代码):
为什么下面的代码在Python中会出现意外的行为?
>>> a = 256
>>> b = 256
>>> a是b
#这是一个预期的结果
这不是一个预期的结果。为什么要这样做?它只意味着a和b引用的值为256的整数是同一个整数实例。整数在Python中是不可变的,因此它们不能改变。这应该不会对任何代码产生影响。这是不应该被期待的。它只是一个实现细节。
但是,也许我们应该感到高兴的是,每次我们声明值等于256时,内存中不会有一个新的单独实例。
>>> a = 257
>>> b = 257
>>> a是b
这里发生了什么?为什么这是假的?
看起来我们现在在内存中有两个不同的整数实例,它们的值都是257。由于整数是不可变的,因此这会浪费内存。希望我们没有浪费太多时间。我们可能不是。但是这种行为并不能保证。
>>> 257等于257
True #但文字数字比较正确
好吧,这看起来像是你的特定Python实现试图变得聪明,除非必要,否则不会在内存中创建冗余值的整数。您似乎表明您正在使用Python的参考实现,即CPython。对CPython很好。
如果CPython能在全局范围内做到这一点,如果它能做到这一点(因为查找会有成本),那就更好了,也许另一个实现可以做到。
但至于对代码的影响,您不应该关心一个整数是否是一个整数的特定实例。您应该只关心该实例的值是什么,并且您可以使用普通的比较操作符,即==。
它做什么
是检查两个对象的id是否相同。在CPython中,id是内存中的位置,但它可以是其他实现中的其他唯一标识数字。用代码重申:
>>> a is b
和
>>> id(a) == id(b)
为什么我们要用is呢?
This can be a very fast check relative to say, checking if two very long strings are equal in value. But since it applies to the uniqueness of the object, we thus have limited use-cases for it. In fact, we mostly want to use it to check for None, which is a singleton (a sole instance existing in one place in memory). We might create other singletons if there is potential to conflate them, which we might check with is, but these are relatively rare. Here's an example (will work in Python 2 and 3) e.g.
SENTINEL_SINGLETON = object() # this will only be created one time.
def foo(keyword_argument=None):
if keyword_argument is None:
print('no argument given to foo')
bar()
bar(keyword_argument)
bar('baz')
def bar(keyword_argument=SENTINEL_SINGLETON):
# SENTINEL_SINGLETON tells us if we were not passed anything
# as None is a legitimate potential argument we could get.
if keyword_argument is SENTINEL_SINGLETON:
print('no argument given to bar')
else:
print('argument to bar: {0}'.format(keyword_argument))
foo()
打印:
no argument given to foo
no argument given to bar
argument to bar: None
argument to bar: baz
我们可以看到,通过is和哨兵,我们能够区分什么时候bar被无参数调用,什么时候它被无参数调用。这些是is的主要用例——不要用它来测试整数、字符串、元组或其他类似的东西是否相等。