“是”运算符的意外行为与整数

为什么在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

这不是预期的结果。 为什么预期? 它只意味着由ab引用的值为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 -不要用它来测试整数,字符串,元组,或者其他喜欢这些东西的平等。

链接地址: http://www.djcxy.com/p/10039.html

上一篇: "is" operator behaves unexpectedly with integers

下一篇: $var not greater than and not lesser than character length