在Python语言中,对象是通过引用传递的。在赋值时,不管这个对象是新创建的,还是一个已经存在的,都是将该对象的引用(并不是值)赋值给变量。
要保持追踪内存中的对象,Python使用了引用计数这一简单技术。也就是说Python内部记录着所有使用中的对象各有多少引用。一个内部跟踪变量,称为一个引用计数器。每个对象各有多少个引用,简称引用计数。当对象被创建时,就创建了一个引用计数,当这个对象不再需要时,也就是说,这个对象的引用计数变为0时,它被垃圾回收。
当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为1。当同一个对象(的引用)又被赋值给其他变量时,或作为参数传递给函数、方法或类实例时,或者被赋值为一个窗口对象的成员时,该对象的一个新的引用,或者称作别名,就被创建(则该对象的引用计数自动加1)。
当对象的引用被销毁时,引用计数会减少。最明显的例子就是当引用离开其作用范围时,这种情况最经常出现在函数运行结束时,所有局部变量都被自动销毁,对象的引用计数也就随之减少。当变量被赋值给另外一个对象时,原对象的引用计数也会自动减1。其他造成对象的引用计数减少的方式包括使用del
语句删除一个变量,或者当一个对象被移出一个窗口对象时(或该容器对象本身的引用计数变成了0时)。
>>> a = 1
>>> b = a
>>> id(a), id(b)
(1386769424, 1386769424)
>>> a = 2
>>> a, b
(2, 1)
>>> id(a), id(b)
(1386769456, 1386769424)
在上面的例子中,a = 1
使变量a
指向了整型对象1
,b = a
使变量b
也指向了整型对象1
,通过语句id(a), id(b)
可以看到变量a
和变量b
所指向的内存地址相同。
语句a = 2
将新的对象2
赋值给了变量a
,此时变量a
指向了对象2
,变量b
仍然指向原来的对象1
。通过语句id(a), id(b)
可以看到变量a
与变量b
所指向的内存地址不同。
Python语言中的对象分为可变对象和不可变对象。
不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟出新的地址,通俗点说就是原地改变。
数字、字符串和元组是不可变类型,列表和字典是可变类型。
>>> a = 1
>>> id(a)
1386769424
>>> a = a + 1
>>> a
2
>>> id(a)
1386769456
在上面的例子中,语句a = a + 1
改变变量a
的值时,由于变量a
所指向的对象1
是不可变对象,因而创建了新的对象2
并使变量a
指向了对象2
。
>>> a = (1, 2, 3)
>>> id(a)
305934860864
>>> a = a + (4, 5, 6)
>>> a
(1, 2, 3, 4, 5, 6)
>>> id(a)
305934762152
可以看到使用元组的结果与整型对象相同。
>>> a = [1, 2, 3]
>>> b = a
>>> id(a), id(b)
(305934751624, 305934751624)
>>> b += [4, 5, 6]
>>> a, b
([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6])
>>> id(a), id(b)
(305934751624, 305934751624)
上面是可变对象的一个例子,变量a
和变量b
同时指向了列表对象[1, 2, 3]
,当语句b += [4, 5, 6]
改变变量b
的值时,并没有创建新的列表对象,而是直接改变了原先变量a
和变量b
所指向的列表对象。
在Python中a += b
和a = a + b
是有区别的。a += b
直接对变量a
进行操作,会改变变量a
的值。a = a + b
中,a + b
会生成一个新的对象,变量a
将指向这个新对象。
>>> a = [1, 2, 3]
>>> b = a
>>> id(a), id(b)
(305934751176, 305934751176)
>>> b = b + [4, 5, 6]
>>> a, b
([1, 2, 3], [1, 2, 3, 4, 5, 6])
>>> id(a), id(b)
(305934751176, 305939124168)
Python中会为每个出现的对象分配内存,哪怕他们的值完全相等(注意是相等不是相同)。如执行a = 2.0
,b = 2.0
这两个语句时会先后为2.0
这个Float
类型对象分配内存,然后将a
与b
分别指向这两个对象。所以a
与b
指向的不是同一对象:
>>> a = 2.0
>>> b = 2.0
>>> a is b
False
>>> a == b
True
但是为了提高内存利用效率对于一些简单的对象,如一些数值较小的int
对象,Python采取重用对象内存的办法,如执行a = 2
,b = 2
时,由于2
作为简单的int
类型且数值小,Python不会两次为其分配内存,而是只分配一次,然后将a
与b
同时指向已分配的对象:
>>> a = 2
>>> b = 2
>>> a is b
True
如果赋值的不是2
而是大的数值,情况就跟前面的一样了:
>>> a = 5555
>>> b = 5555
>>> a is b
False
>>> id(a)
12464372
>>> id(b)
12464396
两个对象即便值相等,但仍然是不同的对象。
>>> id(12345)
305935347440
>>> id(12345)
305935347152
参考文献
[1] Python核心编程(第二版)
[2] https://www.cnblogs.com/sun-haiyu/p/7096918.html
[3] https://blog.csdn.net/weixin_36250487/article/details/79620874