为什么那么以下表现出行为意外在 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运算符可以确定它们的区别。为什么有漏抽象?什么是更好的方法比较两个任意对象以查看它们是否相同时,它们是否是数字我不事先知道?

2008-11-20 18:21:16
问题评论:

很意外,但另一种圆形;认为错误的事情︰ 很意外,等同是如果您分配给这些通过a = 256; b = 256,因为两者都对不同的整数对象分配它们。我希望他们能完全相同,如果您还没有如a = 257; b = aa = b = 257,和事实上,它们是。

这只是 scares 我 !我试图找到 PHP 的相同"==="比较运算符等效在 python 和查找,即使"是"的特点。最近我发现的解决方案是一个冗长这样"type(x) 是 int 和 x = = 100"-它是任一或断言语句;Python 是刚刚调试痛苦随时都会出现。

@DanielSokolowski︰ 没有精确的 Python 等效项的 PHP ===运算符,并且因为 Python 是一种强类型的语言,虽然 PHP 是弱类型化。我的经验,在 Python 是更预测比 PHP 在这方面。

@Greg Hewgill︰ 这是一个误解,python 充其量是比 PHP 只稍强类型化。在 Python 中,您可以执行"'1' = = 1",和更多有线"1 = = foo.bar"栏,是一种功能。如果一种语言强类型化的操作不允许因为它涉及到隐式强制转换,隐式转换导致陷阱。在真正强类型化语言请参见 Haskell。无论如何,我可以使用 === 在 python:) 避免这些陷阱。haskell.org/haskellwiki/Why_Haskell_matters#Strong_typing

@DanielSokolowski: Python 不执行任何隐式转换。虽然您可以评估'1' == 1,结果始终为False (因为字符串永远不能等同于整数)。

回答:

看看这︰

>>> 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"运算符的哈希函数。"Is"运算符是检测到同一对象、 不相等或哈希相等的多个引用 (别名)。例如对于 = b = 20007,是 b为 true。事实是,它适用于单独分配给小整数是刚刚实现详细信息。

请注意,许多语言具有此特点。(我知道 java 的作用)。

@Adam,不正确。文档的同时 idis说它们基于对象标识。既不提及任何类型的哈希算法、 基于价值的或以其他方式。哈希基于__hash__方法。

__hash__默认情况下,使用id ,但不是必须。

@LaraDougan 实际上,词典等的哈希函数调用 hash()

这取决于是否要查找 2 件事相等,或同一个对象。

检查是否为同一对象,不只是等于"是"。小的 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__属性指定的行为。

我的目标是要进行比较的对象标识,而不是值的相等性。除了的数字,想要将对象标识为相同值的相等性。

在这种情况下,我建议您生成自定义的比较函数检查类型的操作数并使用或适当地 = =。

可以签入源代码文件intobject.c,Python 会缓存以提高效率的小整数。每次创建一个小的整数的引用,您指的缓存的小整数,即不是一个新的对象。257 不小整数,因此它将计算为不同的对象。

最好使用"= ="为实现此目的。

我认为您的假设是正确的。试验 id (标识对象的).

In [1]: id(255)
Out[1]: 146349024

In [2]: id(255)
Out[2]: 146349024

In [3]: id(257)
Out[3]: 146802752

In [4]: id(257)
Out[4]: 148993740

In [5]: a=255

In [6]: b=255

In [7]: c=257

In [8]: d=257

In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)

它会显示该号码 < = 255 被视为原义字符,以不同的方式对待任何上述 !

过帐之前我正在 Aack。良好的分析,我认为您可能是正确的。

您忘了测试 256。默认情况下,共享-5 和 256 (含) 之间的整数值。

@Rhymoid,256 也是同样的 255

摘要︰

Python 的"is"运算符的意外行为的整数?

这不是的行为应有任何期望有关,但要知道这种情况,您需要了解以下。

首先,is什么?是比较运算符。文档:

运算符isis not测试对象标识︰ 真当且仅当x is y是 x 和 y 为同一对象。x is not y产生反向的真假值。

因此,以下是等效的。

>>> a is b
>>> id(a) == id(b)

文档:

id返回一个对象的"身份"。这是整数 (或长整数) 这将保证在其生存期内是唯一的并且该对象的常量。两个非重叠的寿命可能会具有相同的id()值。

请注意,CPython (Python 的参考实现) 中的对象的 id 不在内存中的位置这一事实的实现详细信息。Python (例如 Jython 或 IronPython) 的其他实现可以轻松拥有不同id的实现.

那么什么是用例的is介绍了 PEP8:

如无始终应该与单一实例的比较是或不,永远不会是相等运算符。

问题

问︰ 和状态,(使用代码) 的以下问题︰

为什么那么以下表现出行为意外在 Python 中?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result

它是无法预期的结果。预计为何?这只表示整数值从256所引用的两个a,和b是同一个实例的整数。整数是不可变的在 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,这是单一实例 (在内存中的一个位置中存在的唯一实例)。我们可能会创建其他单一实例则可能会以 conflate 他们,我们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和标记,以便我们将能够区分bar将调用不带任何参数,并调用与None这些都是主要的用例is-使用它来测试是否相等的整数、 字符串、 元组或其他诸如此类的事情。

请输入您的翻译

“is” operator behaves unexpectedly with integers

确认取消