有人可以通过id
命令帮助我理解与python中的值相关联的内存空间的概念:并澄清我对下面代码片段的疑虑:
案例1:
>>> a=10
>>> b=10
>>> id(a)
140732845308032
>>> id(b)
140732845308032
案例2: 但同时请看下面的行和python解释器的输出:
>>> c=257
>>> d=257
>>> id(c)
140732848419768
>>> id(d)
140732848419840
我还注意到包含0-256值的变量的id具有相同的id值,但超出此范围会开始变化。
另一个问题是我们可以通过任何机会修改变量的id值。建议我。
先谢谢。
答案 0 :(得分:1)
您无法修改id
。对象的id
在实例化时分配,并且在对象的生命周期内不能更改。在CPython(标准解释器)中,它实际上是对象的内存地址,因此很明显它为什么不能改变;在其他一些Python实现中,它只是一个自动递增的整数,但你仍然无法改变它。
Python有时会重用不可变对象,尽管这是CPython独有的实现细节,而其他Pythons(甚至不同版本的CPython)可能表现不同。由于整数对象2
无法更改,因此在任何给定情况下使用哪个实例并不重要;为了提高效率,Python因此保留了一小部分"整数而不是总是创造新的整数。 (目前这些是-5到256,包括在内。)
字符串是另一种可以重复使用的对象。字符串文字总是被重用,您可以强制其他字符串与intern()
函数一起使用。
空元组是另一个不可再生的对象,它被重用并始终具有相同的id
。
如果您需要更改对象的id
,并且它不是Python始终视为单例的那些对象之一,您会怎么做?复制它。对于某些类型,使用类型本身很容易完成;例如list(my_list)
会创建具有不同ID的my_list
副本。这是浅层副本:如果列表包含子列表,则不会复制这些副本,而是在原始列表和副本中同时显示对相同子列表的引用。
也可以使用切片表示法轻松复制列表对象,la my_list[:]
(如元组和其他序列一样)。
如果您需要完整副本,或者想要复制没有内置复制方式的对象,copy
模块可以让您满意。
答案 1 :(得分:1)
id()
函数给出了CPython中对象的内存地址。
但是,如果我们查看the implementation,我们可以看到-5 <= x < 257
范围内的整数如何在内存中引用相同的地址。
这是通过启动它们的静态数组来完成的:
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
在定义限制后调用:
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
现在,当创建一个新整数时,将调用PyLong_FromLong
函数并调用此CHECK_SMALL_INT
。
然后该函数检查条件:
-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS
在调用最终将查找的get_small_int
之前是数组中的值:
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS]
就是这样,所以-5 <= x < 257
范围内的整数都引用内存中的相同位置,但其他较大的整数都有自己独立的实例;从而导致id()
函数的结果发生变化。