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)
帮助我解决这种身份危机.
更奇怪的是:
>>> 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开始出现一种模式.也许这是由于每个新内存页面开头的一些伏都教会计.
完全允许Python的每个实现在任何程度上进行优化(包括......根本没有;-) 不可变对象的标识和分配(例如数字,元组和字符串)[[对于可变对象没有这样的自由度,如列表,词组和集]].
这两件不更改对象引用之间a以及b所有的实现必须保证的是:
id(a) == id(b),AKA a is b,必须始终暗示a == ba != 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实现,或者至少某些与特定实现紧密绑定的内容,例如调试系统).
你的第四个问题,“在上面的例子中,两个和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)就像它们可能是符号名称。
| 归档时间: |
|
| 查看次数: |
603 次 |
| 最近记录: |