Python内置的id函数其实非常简单,就是将参数对象的内存地址返回,即id函数返回的是一个很大的整数(地址)。基于Python语言的特性,本文做了几个测试,还比较有趣。
相同整数的id相同
>>> a = 6
>>> b = 6
>>> id(a)
94061989787808
>>> id(b)
94061989787808
>>> a == b
True
>>> a is b
True
a和b是两个变量,但是按照上面代码的显示,a和b不仅内容相同,地址也相同,a就是b。(==和is的区别)
这是Python为了高效利用内存而采取的一种机制,a和b都是对一个内存中对象的引用,赋值(=)实际上是创建一个对象,将地址给引用变量。既然a和b指向的对象都是6这个整数,Python就没有“动力”去创建多个对象了。有人会问,如果修改变来领的值,比如修改a的值为7,b的值会跟着变吗?答案是不会。看下面的代码:
>>> a = 7
>>> a
7
>>> b
6
>>> id(a)
94061989787840
>>> id(b)
94061989787808
>>> a == b
False
>>> a is b
False
当 a=7 时,Python实际上是创建了一个新的值为7的整数对象,让a引用,同时保持6这个整数对象不变,这时,a和b的指向地址就不再相同了。
这是Python跟C很不一样的一个地方。在Python中,一切都是对象,所有变量都是对某个对象的应用(有点像指针),内存管理自动进行(某个对象的引用数为0的时候,自动清理这部分内存)。在C中,一切都是内存和指针,C编码在某种意义上是面向内存的编码,任何变量以及函数的返回值,都要明确定义类型,类型就是占用内存的大小。
相同整数的id不同
>>> i1 = 666666
>>> i2 = 666666
>>> i1 == i2
True
>>> i1 is i2
False
>>> id(i1)
140693768352752
>>> id(i2)
140693768353040
如果两个整数值比较大,id就不一样了。我也不明白为什么?数值小,id一样,数值大,id不一样。
相同浮点数的id不同
貌似浮点数,id总是不同的,这应该与浮点数的比较有关系(不能直接用==来比较浮点数)。
>>> f1 = 1.23
>>> f2 = 1.23
>>> f1 is f2
False
>>> f1 == f2 # not right to compare like this
True
>>> id(f1)
140693769437496
>>> id(f2)
140693769437304
返回函数局部变量
如上文所述,Python中所有的变量都是像C语言的指针一样,是一个指向对象的引用,Python在返回函数的局部变量的时候,返回的也是这个局部变量的引用地址。
>>> def test():
... a = 12345
... print(id(a))
... return a
...
>>> b = test()
140693768353008 # id(a) in test()
>>> id(b)
140693768353008
test函数执行完毕后,将a的内存地址返回给了b。
这一个细节在一开始,还困扰了我一小会儿。函数的局部变量的地址处于调用栈内,在函数执行完成后,调用栈就会被弹出,局部变量的地址就失效了,不能在引用了。为什么Python不是这样呢?
Python确实没有为b重新创建一个新的对象,a对象对应的地址被传给了b,但是a这个变量也随着test函数执行完毕而消失了(a被弹出了调用栈,而不是a指向的内存对象被弹出,这段内存可理解在heap中)。Python中的变量,像指针,但却不是指针,只是对象的引用。a的有效范围在test函数内,test函数执行完后,a就不复存在,b获取了test函数的“返回值”,我们没有必要纠结b的内存地址是否与局部变量a一样。Python自己管理内存,我们编程者不需要太关心。a不能再使用了,但是a指向的内存对象还可以继续被使用,这并没有违背函数调用栈的逻辑(a本身被弹出栈,a指向的内存对象还在别的地方存在着,只要还有引用)。
Python内置的id函数一般情况下,没有什么用于,主要用于调试等场景。
-- EOF --