变量的“id()”是否在变量生存期结束后分配给它的值?

2024-06-26 13:43:16 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用以下代码在Python中修补对象的标识:

def f(var1):
    print 'Within f and BEFORE modification: var1= '+str(var1)+', id= '+str(id(var1))
    var1 = 10
    print 'Within f and AFTER modification: var1= '+str(var1)+', id= '+str(id(var1))

def f2(var1):
    print 'Within f and BEFORE modification: var1= '+str(var1)+', id= '+str(id(var1))
    var1 = 1
    print 'Within f and AFTER modification: var1= '+str(var1)+', id= '+str(id(var1))

def f3(var1):
    print 'Within f and BEFORE modification: var1= '+str(var1)+', id= '+str(id(var1))
    var1 = 10
    print 'Within f and AFTER modification: var1= '+str(var1)+', id= '+str(id(var1))


var = 5

print '\n f - var1=10:'
print 'BEFORE FUNCTION CALL: var= '+str(var)+', id= '+str(id(var))
f(var)
print 'AFTER FUNCTION: var= '+str(var)+', id= '+str(id(var))

print '\n f2 - var1=1:'
var = [4,3,1,6]
print 'BEFORE FUNCTION CALL: var= '+str(var)+', id= '+str(id(var))
f2(var)
print 'AFTER FUNCTION: var= '+str(var)+', id= '+str(id(var))

print '\n f3 - var1=10 again:'
var = 7
print 'BEFORE FUNCTION CALL: var= '+str(var)+', id= '+str(id(var))
f3(var)
print 'AFTER FUNCTION: var= '+str(var)+', id= '+str(id(var))

print '\n f2 - var1=1 again:'
var='a'
print 'BEFORE FUNCTION CALL: var= '+str(var)+', id= '+str(id(var))
f2(var)
print 'AFTER FUNCTION: var= '+str(var)+', id= '+str(id(var))

输出:

^{pr2}$

我知道一个对象的标识在其生存期内是唯一的,并且两个具有非重叠生存期的对象可能具有相同的id()值。在

从中我了解到,在不同变量的代码执行期间,我可以得到相同的id(),但是我很惊讶在我的代码中相同的id()值也与变量值一致。在

我的意思是,对于var1=10,我总是得到相同的id()值。赋值var1=1也有同样的情况,它有自己的id()值。即使在不同的函数中执行此赋值,也会返回相同的id()。在

所以我的问题是: Python是否保存了以前的变量、值和标识的记录,即使它们的生命周期已经过期?在

如果在代码中有一个变量赋值与先前过期的变量具有相同的值,那么Python是否会检查内存中先前过期变量的记录,并对相同的内存值优先使用相同的id()?在

我想了解更多关于id()值重用和Python程序中内存管理的知识。在


Tags: and对象代码idvarfunctioncallf2
3条回答

简短回答您的问题-Python缓存范围为[-5256]的整数。在

因此,无论何时执行var1 = 10var1 = 1,总是从整数缓存中获取相同的对象,这就是为什么即使在函数的不同运行中也会看到相同的id。在

如果对大于或等于257的值进行尝试,可能会看到不同的结果。在

一个非常简单的行为例子-

>>> var1 = 257
>>> var2 = 257
>>> id(var1)
36635792
>>> id(var2)
36636304
>>> var1 = 10
>>> var2 = 10
>>> id(var1)
1642386016
>>> id(var2)
1642386016

文档是正确的,但是生存期和对象标识可能会有点混淆。当赋值给一个变量时(无论是普通的还是扩充的)意味着它后面的变量引用赋值右边的对象(可能是另一个对象)。这就是赋值后id(x)发生变化的原因。在

还请注意,解释器可能会将对对象的引用隐藏在背后,这意味着对象的生存期可能比预期的长(在某些情况下甚至与解释器的生存期一样长),或者比您预期的更早创建。在某些情况下,可以再次访问这些对象。在

例如,一些(目前我认为是-5到+256)整数就在这些对象中(所以在您编写x=1之前,对象{}已经存在)。注意,否则相等的整数不必是相同的(即x==y并不意味着x is y),这意味着存在冗余对象。在

另一个例子是internetd strings,通常情况下,解释器为了提高效率而对字符串进行内接(所以在编写x="keys"之前,对象{}已经存在,因为这是python internes的字符串之一)。在

第三个例子是,对象可以在读取编译后的python代码时创建,这意味着数字对象甚至可以在代码开始执行之前创建。这意味着具有相同值的整型文字和字符串文字将是同一个对象-前提是它们是在同一时间编译的。在

请注意,这些对象是不可变的,因此,如果您碰巧再次获得相同的对象,则不会受到伤害(因为它们是不可变的,因此无论它们是新创建的还是重用的,它们都是相等的)。在

另一个问题是函数会缓存其中的所有常量。在

因此

def f():
    return 1.1

assert f() is f() # or id(f()) == id(f())

通过查看函数的代码对象,可以找到与函数关联的缓存常量。在

^{pr2}$

元组通常是数字、字符串或元组。因此,正如您总是为var1分配一个文本整数,编译器知道这是不能更改的常量值,因此在函数调用之间缓存该整数对象。在

我在f中使用float的原因是这是唯一一个将缓存float的实例。字符串和整数也可以在其他情况下缓存,例如

>>> x = 1.1
>>> y = 1.1
>>> x is y
False

更新

虽然元组在Python中被认为是不可变的,但在实现级别上,它们只是另一个可变内存。有时,如果Python知道没有其他人可以访问这个tuple,它会对一个tuple进行变异。例如

>>> [id(x) for x in zip("abc", range(3))]
[12684384, 48723328, 12684384] # different tuples

>>> l = []
>>> for x in zip("abc", range(3)):
    l.append(id(x))
    del x # only the zip iterator has access to yielded tuple now

>>> l
[12684384, 12684384, 12684384]

请参阅定义zip next method的相应行。在

if (Py_REFCNT(result) == 1) { // if we are the only ref holder
    Py_INCREF(result);
    for (i=0 ; i < tuplesize ; i++) {
        it = PyTuple_GET_ITEM(lz->ittuple, i);
        item = (*Py_TYPE(it)->tp_iternext)(it);
        if (item == NULL) {
            Py_DECREF(result);
            return NULL;
        }
        olditem = PyTuple_GET_ITEM(result, i);
        PyTuple_SET_ITEM(result, i, item); // modify in place
        Py_DECREF(olditem);
    }

相关问题 更多 >