Python浅拷贝 深拷贝

内存泄漏太可怕。

Python 可变对象 & 不可变对象

在Python中,对象分为两种:可变对象不可变对象

  • 不可变对象包括int,float,long,str,tuple等;
  • 可变对象包括list,set,dict等

需要注意的是:这里说的不可变指的是值的不可变。对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。另外,不可变的类型可以计算hash值,作为字典的key。可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。
如下所示:

list = [1, 2, 3]
print(id(list))
arr = [4, 5]
list = list + arr
print(id(list))

list2 = [1, 2, 3]
print(id(list2))
list2 += [4, 5]
print(id(list2))

# 输出结果:
# 4557530312
# 4557530696
# 4557530312
# 4557530312
  • list = list + arr,list出现两次,必须执行两次,性能不好,合并必须新建对象list,然后复制两个列表合并,这属于拷贝;
  • list += arr,list只出现一次,因为不生成新对象,因此性能好,只在内存块末尾增加元素。当操作元素为list时,“+=”会自动调用 extend 方法进行合并运算,这属于共享引用.

Python没有赋值,只有引用

Python中的“=”传递的是引用(内存地址),应按照C语言中的指针理解。

Python浅拷贝copy.copy()

Python中,对于非容器类型(如数字、字符串、和其他原子类型的对象)没有被拷贝一说。

使用copy.copy(),可以进行对象的浅拷贝,它复制了对象,但对于对象中的子对象,依然使用原始的引用,所以原始数据改变,子对象会改变。

该示例显示了通过传递引用的方式创建obj1,此时obj1 is list

list = [1, 2, 3, ['a', 'b']]
obj1 = list
# 输出list与obj1的地址
print('id of list =', id(list))
print('id of obj1 =', id(obj1))
# obj1原始的值
print('value of obj1 =', obj1)
# 修改list的值,看obj1中对应的值是否改变
list[1] = 999
print('value of obj1 =', obj1)

# 输出结果:
# id of list = 4367528520
# id of obj1 = 4367528520
# value of obj1 = [1, 2, 3, ['a', 'b']]
# value of obj1 = [1, 999, 3, ['a', 'b']]

接下来通过浅拷贝创建obj2,可以看到此时list与obj2指向了不同的地址,修改list中的字符串、布尔类型、整数、浮点数、数字不会改变obj2中的值;但是此时obj2的子对象还是与list中的子列表共享一块内存

obj2 = copy.copy(list)
# 输出list与obj2的地址
print('id of list =', id(list))
print('id of obj2 =', id(obj2))
# 输出list与obj2中每个元素的地址
print('id of list = ', [id(it) for it in list])
print('id of obj2 = ', [id(it) for it in obj2])
# 修改list[0]的值,看obj2中的值是否改变
list[0] = -1
print('value of list =', list)
print('value of obj2 =', obj2)
print('-' * 50)
# 修改list中的子列表,看obj2中的值是否改变
list[3].append('c')
print('value of obj2 =', obj2)
# 输出list与obj2中每个元素的地址
print('id of list = ', [id(it) for it in list])
print('id of obj2 = ', [id(it) for it in obj2])

# 输出结果:
# id of list = 4367528520
# id of obj2 = 4367558344
# id of list =  [4345380112, 4346236624, 4345380176, 4367558536]
# id of obj2 =  [4345380112, 4346236624, 4345380176, 4367558536]
# value of list = [-1, 999, 3, ['a', 'b']]
# value of obj2 = [1, 999, 3, ['a', 'b']]
# --------------------------------------------------
# value of obj2 = [1, 999, 3, ['a', 'b', 'c']]
# id of list =  [4345380048, 4346236624, 4345380176, 4367558536]
# id of obj2 =  [4345380112, 4346236624, 4345380176, 4367558536]

当我们使用下面的操作的时候,会产生浅拷贝的效果:

  • 使用切片[:]操作
  • 使用工厂函数(如list/dir/set)
  • 使用copy模块中的copy()函数

Python深拷贝copy.deepcopy()

复制一个容器对象,并且,以及它里面的所有元素(包含元素的子元素),可以使用copy.deepcopy()进行深拷贝

list = [1, 2, 3, ['a', 'b']]
obj3 = copy.deepcopy(list)
# 输出list与obj3的地址
print('id of list =', id(list))
print('id of obj3 =', id(obj3))
# 输出list与obj3中每个元素的地址
print('id of list = ', [id(it) for it in list])
print('id of obj2 = ', [id(it) for it in obj3])
# 修改list中的子列表,看obj2中的值是否改变
list[3].append('c')
# 修改list[0]的值,看obj2中的值是否改变
print('value of list =', list)
print('value of obj2 =', obj3)

# 输出结果:
# id of list = 4370554760
# id of obj3 = 4370650248
# id of list =  [4348476688, 4348476720, 4348476752, 4370650440]
# id of obj2 =  [4348476688, 4348476720, 4348476752, 4370650120]
# value of list = [1, 2, 3, ['a', 'b', 'c']]
# value of obj2 = [1, 2, 3, ['a', 'b']]

关于Python内存管理机制请见:
Python源码阅读-内存管理机制(一)
Python源码阅读-内存管理机制(二)

    原文作者:ChongmingLiu
    原文地址: https://www.jianshu.com/p/d0abb27a7bfc
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞