“是”运算符的意外行为与整数
为什么在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
操作者可以分辨出来。 为什么有漏洞的抽象? 当我事先不知道数字是否是数字时,比较两个任意对象的更好方法是看它们是否相同?
看看这个:
>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828
编辑:这是我在Python 2文档中发现的“纯整型对象”(对于Python 3,它是相同的):
当前实现为-5到256之间的所有整数保留一个整数对象数组,当您在该范围内创建一个int时,您实际上只是返回对现有对象的引用。 所以应该可以改变1的值。我怀疑在这种情况下Python的行为是未定义的。 :-)
这取决于你是否想要看看两件事是否相同,或者是同一个对象。
is
检查,看看他们是否是同一个对象,不只是平等的。 小型整数可能指向相同的存储位置以提高空间利用率
In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144
您应该使用==
比较任意对象的相等性。 您可以使用__eq__
和__ne__
属性指定行为。
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
is not
测试对象的身份:当且仅当x和y是同一个对象时, x is y
是真的。 x is not y
产生逆真值。
因此以下是等同的。
>>> a is b
>>> id(a) == id(b)
从文档:
id
返回一个对象的“身份”。 这是一个整数(或长整数),在整个生命周期中保证这个对象是唯一的并且是常量。 具有非重叠生命周期的两个对象可能具有相同的id()
值。
请注意,CPython中的对象的id(Python的参考实现)是内存中的位置是一个实现细节。 Python的其他实现(例如Jython或IronPython)可以轻松地为id
创建不同的实现。
那么用例is
什么? PEP8描述:
像None
这样的单身人士的比较应该总是用“ is
或“ is not
,从来is not
平等运算符。
问题
你问,并说,以下问题(与代码):
为什么在Python中以下行为意外?
>>> a = 256
>>> b = 256
>>> a is b
True # This is an expected result
这不是预期的结果。 为什么预期? 它只意味着由a
和b
引用的值为256
的整数是整数的相同实例。 整数在Python中是不可变的,因此它们不能改变。 这应该对任何代码都没有影响。 它不应该被期望。 这仅仅是一个实现细节。
但是,也许我们应该感到高兴的是,每次我们声明一个值等于256时,内存中没有新的单独实例。
>>> a = 257
>>> b = 257
>>> a is b
False # What happened here? Why is this False?
看起来我们现在有两个单独的整数值,内存值为257
。 由于整数是不变的,这就浪费了内存。 希望我们不会浪费太多。 我们可能不是。 但是这种行为不能保证。
>>> 257 is 257
True # Yet the literal numbers compare properly
那么,这看起来像你的Python的特定实现正在努力变得聪明,并不会在内存中创建冗余值的整数,除非必须。 您似乎表示您正在使用Python的参考实现,即CPython。 适合CPython。
如果CPython可以在全球范围内做到这一点,如果它可以做到这么便宜(因为在查找中会花费一些代价),也许还有另一种实现可能会更好。
但是至于对代码的影响,您不应该在乎整数是否是整数的特定实例。 你应该只关心那个实例的值是什么,并且你会使用普通的比较运算符,即==
。
什么is
is
检查该id
的两个对象是相同的。 在CPython中, id
是内存中的位置,但它可能是另一个实现中的其他唯一标识号。 用代码重新说明这一点:
>>> a is b
是相同的
>>> id(a) == id(b)
为什么我们要使用is
呢?
相对来说,这可以是一个非常快速的检查,检查两个非常长的字符串是否相等。 但由于它适用于对象的唯一性,因此我们对它的使用情况有限。 实际上,我们主要想用它来检查None
,它是一个单例(存储在一个地方的唯一实例)。 我们可以创建其他单身是否有潜力,他们混为一谈,我们可能与检查is
,但这些都是比较少见的。 这里有一个例子(将在Python 2和3中工作)例如
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
和sentinel,我们可以区分何时调用没有参数的bar
和何时使用None
调用它。 这些是主要的用例的is
-不要用它来测试整数,字符串,元组,或者其他喜欢这些东西的平等。
上一篇: "is" operator behaves unexpectedly with integers
下一篇: $var not greater than and not lesser than character length