python 的内存回收 || 及深浅Copy详解
一、python中的变量及引用
1.1 python中的不可变类型:数字(num)、字符串(str)、元组(tuple)、布尔值(bool<true,false>)
- 不可变对象的原因: 都知道python中一切都是对象,而变量就是这些对象的引用, 综合表述: 变量是一个系统表的元素,拥有指向对象的连接的空间
对象是被分配的一块内存,存储其所代表的值【对象是内存,内存上存储着值】
引用是自动形成的从变量到对象的指针
特别注意: 类型属于对象,不是变量
c = 17 #1 数字17就是一个对象,实实在在存在计算机内存中 d = c #2 c 和 d 都是对象17的一个引用,c指向17,d也是 id(c) #3 1462698960 id(d) #4 1462698960
在#1 处我们定义了各一个变量c,c指向了17(把17赋值给c),对象17的一个引用c
然后在#2处,又定义了一个变量d ,把c赋值给了d,接着#3、#4查看了c、d的 id 相同,
发现是同一个对象(17),对象17的引用+1
引用:
对象17的引用现在有两个了
变量:
在内部,变量事实上是到对象内存空间的一个指针
1.2 python中内存回收机制
1.2.1 python本身是一门动态语言 与c/c++ /java不同,不需要事先定义变量开辟内存空间,然后给变量赋值,存储到变量的内存空间中。使用结束,当然也不需要你去手动调用析构函数释放内存了。 python会预先申请一部分内存空间,在运行时定义了变量-对象,根据对象确认它的type,将对象放到申请的内存中,python每过一段时间就来检查一次,当有对象的引用为0时,就回收这块内存,返还回先申请的内存空间,而不是计算机。这样避免了内存碎片过多问题。1.2.2 怎么减少对象的引用
1.将变量引用指向其他对象
2.删除变量(对象的引用)
总结:回收机制为判断对象引用是否为0,如果为零就回收内存到自己申请的内存空间,不是计算机硬盘。
1.3 再谈不可变类型
当定义变量为数字、字符串、tuple、布尔值时,这些变量所对应的对象在内存空间的值是不可改变了,你重新赋值,也只是把变量引用指向了另一个对象,id变了,本身那个对象是不可变的。
二、Python 直接赋值、浅拷贝和深度拷贝解析
-
直接赋值:其实就是对象的引用(别名)。【变量其实被赋值的是对象的内存地址】
-
浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。【内部的子对象依然是源对象的赋值--》浅拷贝之后的内部子对象依然指向源对象中的子对象】
字典浅拷贝实例 a = {1: [1,2,3]} b = a.copy() a, b ({1: [1, 2, 3]}, {1: [1, 2, 3]}) a[1].append(4) a, b ({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
-
深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。【形成了完完全全新的对象--》不会在随源对象改变而改变,而是有了自己的生命】
深度拷贝需要引入 copy 模块: 实例 import copy c = copy.deepcopy(a) a, c ({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]}) a[1].append(5) a, c ({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})
解析
1、b = a: 赋值引用,a 和 b 都指向同一个对象。
2、b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。
b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
以下实例是使用 copy 模块的 copy.copy( 浅拷贝 )和(copy.deepcopy ): 实例 #!/usr/bin/python # -*-coding:utf-8 -*- import copy a = [1, 2, 3, 4, ['a', 'b']] #原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy.deepcopy(a) #对象拷贝,深拷贝 a.append(5) #修改对象a a[4].append('c') #修改对象a中的['a', 'b']数组对象 print( 'a = ', a ) print( 'b = ', b ) print( 'c = ', c ) print( 'd = ', d )
以上实例执行输出结果为:
('a = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
('b = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
('c = ', [1, 2, 3, 4, ['a', 'b', 'c']])
('d = ', [1, 2, 3, 4, ['a', 'b']])
三、总结
- 深浅拷贝都是对源对象【不是对象的内存地址,而是对象本身】的复制,占用不同的内存空间
- 如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
- 如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
- 序列对象的切片其实是浅拷贝,即只拷贝顶级的对象
去期待陌生,去拥抱惊喜。