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']])
三、总结
  • 深浅拷贝都是对源对象【不是对象的内存地址,而是对象本身】的复制,占用不同的内存空间
  • 如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
  • 如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
  • 序列对象的切片其实是浅拷贝,即只拷贝顶级的对象
posted @ 2021-04-26 10:58  习久性成  阅读(666)  评论(0编辑  收藏  举报