前言
在 Python 中提供了一个用于拷贝操作的 copy 模块,该模块中提供 浅拷贝
和 深拷贝
两种操作,其中:
- 浅拷贝:只是将对象管理地址进行打包复制,其内部的元素的并未进行复制,而是使用旧的存储地址。
- 深拷贝 :是将对象中的所有元素进行复制,然后重新开辟空间来存储。
对象和内存
对象和内存中的值是映射的关系,当需要取内存中的值的时候,需要通过其变量名去获取。
内存中的值是单一的,但其映射的对象可以是多个,所以某一个对象修改其内存的值后,其所有的映射到该内存上的对象都会刷新到最新的值。
实例如下:
>>> a = [1,2,3] # 定义一个变量(对象)
>>>
>>> b = a # 将变量的 a 的值赋给变量 b
>>> id(a) # 查看 a 的存储内存空间
3070831436L
>>> id(b) # 查看 b 的存储内存空间
3070831436L
>>>
>>> a.append(4) # 给 a 对象列表,添加一个新的元素 ‘4’
>>> a # 查看 a 的值
[1, 2, 3, 4]
>>> b # 查看 b 的值
[1, 2, 3, 4]
>>>
通过以上可以看到,不同变量之间,相同的值存储的内存空间是一样的;当使用 a 变量修改其内容时,b 变量的值也会放生改变。
浅拷贝
特点:浅拷贝只复制变量的整体引用地址,不会复制变量内部元素的地址。当内部元素发生变化时,复制后的值和会随着赋值对象的变化而变化。
实例如下:
>>> import copy
>>> a = [1,2,['a','b'],3] # 定义嵌套列表
>>> b = copy.copy( a ) # 复制a(浅拷贝)
>>> a
[1, 2, ['a', 'b'], 3]
>>> b
[1, 2, ['a', 'b'], 3]
>>> id(a)
3070610124L # 查看 a 与 b 变量的地址
>>> id(b)
3070631500L
\#查看列表 a 与 b 中第 2 个元素的地址:
>>> id(a[1])
163477668
>>> id(b[1])
163477668
\#查看列表 a 与 b 中的第 3 个元素(嵌套列表)的地址:
>>> id(a[2])
3070631436L
>>> id(b[2])
3070631436L
#可见它们的地址还是同一个存储地址
\# 接下来是改变 a 的值,然后再查看 b 的值:
>>> a[2].append('c')
>>> a
[1, 2, ['a', 'b', 'c'], 3]
>>> b
[1, 2, ['a', 'b', 'c'], 3]
分析:通过观察可知 浅拷贝时,当改变元素内部的值时,复制后对象的值,会随着赋值前对象值的变化而变化。
由于数字类型为不可变类型,所以复制前后的存储位置是一致的,并不存在同值不同存储位置的情况。
因为 数字类型 为不可变类型,所以这里不加以比较!
提示:在大列表中 添加/修改 元素,复制后的值不受影响。
深拷贝
特点:深拷贝会将数据全部复制,然后重新开辟新的空间来储存该值。所以,复制后的值都是相对独立的,后期使用并不受复制前的对象所影响。
基本语法:
deepcopy(x, memo=None, _nil=[])
参数说明:x
是赋值的对象 、memo
和 _nil
参数可缺省。
实例如下:
>>> import copy
>>> a = [4,5,['a','b'],6]
>>> b = copy.deepcopy(a) # 使用深拷贝复制 a 对象
>>> a
[4, 5, ['a', 'b'], 6]
>>> b
[4, 5, ['a', 'b'], 6]
>>>
\#查看列表 a 和 b 中的嵌套列表,可见它们的地址是独立的:
>>> id(a[2])
3070658220L
>>> id(b[2])
3069923596L
\# 修改 a 列表中的嵌套列表,观察其变化:
>>> a[2].append('c')
>>> a
[4, 5, ['a', 'b', 'c'], 6, 'c']
>>> b
[4, 5, ['a', 'b'], 6]
# 可见修改 a 后 b 也不会发生任何的变化
分析:通过以上可见,采用深拷贝操作后,是将被复制对象的所有值进行拷贝,然后重新开辟新的存储单元来存储拷贝后的内容,所以复制后的内容并不受前面被复制对象的改变而改变。
完整学习教程请访问:Python3 入门教程——目录索引