Python身份:多重人格障碍,需要代码缩小

ran*_*guy 10 python memory identity memory-management uniqueidentifier

可能重复:
Python"is"运算符使用整数意外运行

我偶然发现了以下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.)
Run Code Online (Sandbox Code Playgroud)
  1. 每个数字都是唯一的对象吗?
  2. 不同的变量是否保持相同的元素值(例如,2,ii)是同一个对象?
  3. Python生成的数字的id是多少?
  4. 在上面的例子中,是指向保持值为2的存储单元的两个和二个指针吗?那将是非常奇怪的.

帮助我解决这种身份危机.

更奇怪的是:

>>> 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))
Run Code Online (Sandbox Code Playgroud)

上面的代码检查连续整数的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
Run Code Online (Sandbox Code Playgroud)

请注意,从413开始出现一种模式.也许这是由于每个新内存页面开头的一些伏都教会计.

Ign*_*ams 9

-1和255之间的整数(?)以及字符串文字都是实体.源中的每个实例实际上代表相同的对象.

在CPython中,结果id()是PyObject的进程空间中的地址.


Ale*_*lli 8

完全允许Python的每个实现在任何程度上进行优化(包括......根本没有;-) 不可变对象的标识和分配(例如数字,元组和字符串)[[对于可变对象没有这样的自由度,如列表,词组和集]].

这两件不更改对象引用之间a以及b所有的实现必须保证的是:

  1. id(a) == id(b),AKA a is b,必须始终暗示a == b
  2. 因此a != b必须始终暗示id(a) != id(b)AKAa 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实现,或者至少某些与特定实现紧密绑定的内容,例如调试系统).


Tim*_*her 4

你的第四个问题,“在上面的例子中,两个和ii指针是否指向一个保存值2的内存单元?这会非常奇怪”,这确实是理解整个事情的关键。

如果您熟悉 C 等语言,那么 Python“变量”的工作方式实际上并不相同。AC 变量声明如下:

int j=1;
int k=2;
k += j;
Run Code Online (Sandbox Code Playgroud)

说,“编译器,在堆栈上为我保留两个内存区域,每个区域都有足够的空间来容纳一个整数,并记住一个为“j”,另一个为“k”。然后用值“1”填充j k 的值为“2”。” 在运行时,代码表示“获取 k 的整数内容,添加 j 的整数内容,并将结果存储回 k”。

Python 中看似等效的代码:

j = 1
k = 2
k += j
Run Code Online (Sandbox Code Playgroud)

说的是不同的内容:“Python,查找名为“1”的对象,并创建一个名为“j”的标签来指向它。查找名为“2”的对象,并创建一个名为“k”的标签来指向它现在查找对象'k'指向('2'),查找对象'j'指向('1'),并将'k'指向执行'add'操作所产生的对象就两人而言。”

反汇编这段代码(使用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)
Run Code Online (Sandbox Code Playgroud)

所以,是的,Python“变量”是指向对象的标签,而不是可以填充数据的 容器。

其他三个问题都是“Python何时从一段代码创建一个新对象,以及何时重用已有的对象?”的变体。后者称为“实习”;它发生在较小的整数和字符串上,它们看起来(对于 Python)就像它们可能是符号名称。