当你根据一个现有变量 var
去创建一个新变量 var_new
时,最先想到的大概是 var_new = var
了。这个操作也称为赋值。
受限于你的使用场景,你会发现这种操作有的时候有用,有的时候总是出现自己意想不到的结果:改变var_new
,结果发现var
也变了。
示例代码如下:
使用 “=”进行赋值
# 本文代码使用python3.5验证通过
var = [[1,2,3],[4,5,6],[7,8,9]]
var_new = var
var_new[0][0] = 10
print('var id: ', id(var))
print('var_new id: ', id(var_new))
print('var: ', var)
print('var_new: ', var_new)
运行代码,输出结果如下:
var id: 140688176656072
var_new id: 140688176656072
var: [[10, 2, 3], [4, 5, 6], [7, 8, 9]]
var_new: [[10, 2, 3], [4, 5, 6], [7, 8, 9]]
分析运行结果,根据打印出的内存地址也可以看出,两个变量指向了相同的内存地址;打印变量结果,可以看到我们对var_new
的改变值的操作同样影响到了var
。
因为赋值操作只是原变量的引用,反映在内存中,并没有开辟新的空间存放该变量的副本。
那么,当我们想根据一个已有变量创建一个独立的副本时,希望对副本的操作不影响原变量,该怎么办呢?
是的,python当然有解决办法。采用python自带的copy
模块,并且有以下两种方法:
- 浅拷贝(shallow copy)
- 深拷贝(deep copy)
copy模块
copy模块用法不难,假如我们对变量var进行拷贝,示例代码如下:
import copy
var_new1 = copy.copy(var) # 浅拷贝
var_new2 = copy.deepcopy(var) # 深拷贝
那么,这两种方法的区别和相同点是什么呢?首先我们一一讲解两种方法,最后总结。
1 必备知识准备:
python中的数据类型中,列表、字典、元组、集合是存储大量数据的容器,其中的每个元素可以是像 1,2.3,-23.45
,'Hello, World’这样的非容器类的数据类型(也称为原子类型),也可以是列表、字典、元组、集合这样的容器数据。
非容器类的数据类型不存在深拷贝、浅拷贝之说。列表、字典、元组、集合等容器类型才会有深、浅拷贝之说。
约定:对于var = [[1,2,3],[4,5,6],[7,8,9]]
,我们约定[1,2,3]
是var
的某一个一级子对象;而1
是var
的二级子对象,是var[0]
的一级子对象。
2 浅拷贝
先来看浅拷贝用法的示例代码:
import copy
var = [[1,2,3],[4,5,6],[7,8,9]]
var_new = copy.copy(var)
var_new[0][0] = 10
var_new[2] = 'a'
print('var id: ', id(var))
print('var_new id: ', id(var_new))
print('var: ', var)
print('var_new: ', var_new)
运行代码,输出结果如下:
var id: 139788348788424
var_new id: 139788348375816
var: [[10, 2, 3], [4, 5, 6], [7, 8, 9]]
var_new: [[10, 2, 3], [4, 5, 6], 'a']
分析运行结果,根据打印出的内存地址也可以看出,两个变量指向了不同的内存地址,说明这次创建变量确实在内存中创建了副本;打印变量结果,可以看到我们对var_new
的一级子对象的改变不会影响到了var
,但是对var_new
的二级子对象的改变不会影响到了var
。
这就是浅拷贝中浅的含义了。浅拷贝只是拷贝第一层子对象。拷贝的内容包括数字、字符串等非容器子对象,以及列表、字典、元组、集合等容器子对象的引用。
3 深拷贝
老样子,先看深拷贝用法的示例代码:
import copy
var = [[1,2,3],[4,5,6],[7,8,9]]
var_new = copy.deepcopy(var)
var_new[0][0] = 10
var_new[2] = 'a'
print('var id: ', id(var))
print('var_new id: ', id(var_new))
print('var: ', var)
print('var_new: ', var_new)
运行代码,输出结果如下:
var id: 139775218323144
var_new id: 139775217910536
var: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
var_new: [[10, 2, 3], [4, 5, 6], 'a']
分析运行结果,根据打印出的内存地址也可以看出,两个变量指向了不同的内存地址,说明这次创建变量确实在内存中创建了副本;打印变量结果,可以看到我们对var_new
的一、二级子对象的改变不会影响到了var
。
够深吧,深拷贝是把一个对象的所有层级的子对象都在内存中开辟空间,创建了副本。
总结
- Python中,赋值操作"="只是原对象的引用,不会开辟新内存空间。
- Python中,容器类型的数据存在深、浅拷贝之分。
- 如果想根据某对象创建一个完全独立的对象,使用copy.deepcopy(),但创建过程较慢。
- 如果只想复制对象的第一层元素,使用copy.copy()可以降低物理空间占用,时间较快。但需小心使用,新副本并不完全独立于原对象,需注意改变第2层甚至更深层的值对原对象的影响。