可能重复:
Python “is” operator behaves unexpectedly with integers
我偶然发现了以下Python奇怪的事情:
>>> two = 2
>>> ii = 2
>>> id(two) == id(ii)
True
>>> [id(i) for i in [42,42,42,42]]
[10084276, 10084276, 10084276, 10084276]
>>> help(id)
Help on built-in function id in module __builtin__:
id(...)
id(object) -> integer
Return the identity of an object. This is guaranteed to be unique among
simultaneously existing objects. (Hint: it's the object's memory address.)
帮助我解决这种身份危机。
更多的奇怪之处:</ p>
>>> a,b=id(0),id(1)
>>> for i in range(2,1000):
a,b=b,id(i)
if abs(a-b) != 12:
print('%i:%i -> %i' % (i,a,b))
上面的代码检查连续整数的id是否也是连续的,并打印出来 异常:
77:10083868 -> 10085840
159:10084868 -> 10086840
241:10085868 -> 10087840
257:10087660 -> 11689620
258:11689620 -> 11689512
259:11689512 -> 11689692
260:11689692 -> 11689548
261:11689548 -> 11689644
262:11689644 -> 11689572
263:11689572 -> 11689536
264:11689536 -> 11689560
265:11689560 -> 11689596
266:11689596 -> 11689656
267:11689656 -> 11689608
268:11689608 -> 11689500
331:11688756 -> 13807288
413:13806316 -> 13814224
495:13813252 -> 13815224
577:13814252 -> 13816224
659:13815252 -> 13817224
741:13816252 -> 13818224
823:13817252 -> 13819224
905:13818252 -> 13820224
987:13819252 -> 13821224
请注意,从413开始出现一种模式。也许是因为在每个新的记忆页面的开头都有一些巫术会计。
答案 0 :(得分:9)
-1到255之间的整数(?)以及字符串文字被实习。源中的每个实例实际上代表同一个对象。
在CPython中,id()
的结果是PyObject的进程空间中的地址。
答案 1 :(得分:8)
完全允许Python的每个实现在任何程度上进行优化(包括......根本没有;-) 不可变对象(例如数字,元组和字符串)的标识和分配[[可变对象没有这样的自由度,例如列表,字符串和集合]]。
在两个不可变对象引用a
和b
之间,所有实现都必须保证:
id(a) == id(b)
,AKA a is b
,必须始终暗示a == b
a != b
必须始终暗示id(a) != id(b)
AKA a is not b
特别注意没有约束,即使对于不可变类型,a == b
必须暗示a is b
(即id(a) == id(b)
)。只有None
才能保证(因此您始终可以测试if x is None:
而不是if x == None:
)。
当前的CPython实现通过“合并”(具有单个分配,因此单个id
)来处理特定范围内的小整数,以及内置的不可变类型对象,从而利用这些自由度。文字在给定函数中出现不止一次(例如,如果函数f
有四次出现的文字'foobar'
,它们都将引用函数常量中的单个字符串'foobar'
实例与允许的实现相比,节省了一点空间,该实现将存储该常量的四个相同但独立的副本。)
所有这些实现注意事项对于Python编码器来说都是非常小的兴趣(除非您正在处理Python实现,或者至少是某些与特定实现紧密绑定的内容,例如调试系统)。
答案 2 :(得分:4)
你的第四个问题,“在上面的例子中,两个和两个指针指向一个存储单元格的值为2?这将是非常奇怪的”,这是理解整个事情的关键。
如果你熟悉像C这样的语言,Python“变量”实际上并没有那么相同。一个C变量声明,如:
int j=1;
int k=2;
k += j;
说,“编译器,为我保留两个内存区域,在堆栈上,每个区域都有足够的空间来容纳一个整数,并记住一个为'j'而另一个为'k'。然后用值j填充j '1'和k的值为'2'。“在运行时,代码说“取k的整数内容,添加j的整数内容,并将结果存储回k。”
Python中看似相同的代码:
j = 1
k = 2
k += j
说了一些不同的东西:“Python,查找称为'1'的对象,并创建一个名为'j'的标签指向它。查找称为'2'的对象,并创建一个名为'k的标签'指向它。现在查找对象'k'指向('2'),查找对象'j'指向('1'),并将'k'指向对象,因为执行'在两者上添加“操作”。
反汇编此代码(使用 dis 模块)可以很好地显示:
2 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (j)
3 6 LOAD_CONST 1 (2)
9 STORE_FAST 1 (k)
4 12 LOAD_FAST 1 (k)
15 LOAD_FAST 0 (j)
18 INPLACE_ADD
19 STORE_FAST 1 (k)
所以是的,Python“变量”是指向对象的标签,而不是可以填充数据的容器。
其他三个问题都是“Python什么时候从一段代码创建一个新对象,以及何时重用它已经拥有的一个?”的变体。后者被称为“实习”;它发生在较小的整数和字符串上(对Python来说)它们可能是符号名称。
答案 3 :(得分:2)
你应该非常小心这些调查。您正在研究语言实现的内部,并且不能保证这些内容。 id
上的帮助是正确的:两个不同对象的数量不同,同一对象的数量相同。作为实现细节,在CPython中它是对象的内存地址。 CPython可能会决定随时更改此细节。
实施相同分配时间的小整数的细节也是一个可能随时改变的细节。
此外,如果您从CPython切换到Jython,PyPy或IronPython,除id()
上的文档外,所有投注均已关闭。
答案 4 :(得分:1)
并非每个数字都是唯一的对象,而且某些数字是CPython解释器的优化细节。 不要依赖此行为。就此而言,永远不要使用is
来测试相等性。如果您完全确定需要完全相同的对象,请仅使用is
。