在弄清楚Python中的赋值、浅拷贝与深拷贝之前。我们首先要搞清楚,什么样的元素存在这样的问题。
Python中的变量类型
Python中的变量类型可以分为原子、标量两种类型
什么是原子类型呢?我们可以理解为最简单的类型,它包括:数字、字符串。
什么是变量类型呢?标量就是Python为我们提供的标准变量类型,它包括:list, tuple, dict三种。
list, tuple, dict中不仅可以存储原子类型的数据,又可以存储各种标量;而原子类型却只能去给标量类型当小弟。观察这两种类型的变量,我们可以明显的发现标量相对于原子要复杂的多。赋值、浅拷贝与深拷贝之间的区别就存在于逼格很高的老大哥(标量)身上。
原子类型的变量的值当且仅当进行赋值操作时才会改变,而且在进行操作时总是不改变内存上的数据,而是改变变量的指向。而在标量类型中除了不可改变的tuple以外,list与dict类型的变量除了赋值操作还有可以使其改变的操作(如list.append()操作),这些操作都是在其内存上改变数据。
对赋值、浅拷贝与深拷贝的初步认识。
首先要明确,在Python中所有的变量都是引用变量,它们指向的统统都是对象。所以与其说变量,它们更像是一个遥控器。
赋值:
赋值操作是将对象的操作权赋予一个引用变量的操作,赋的值的来源可以是一个对象本身,也可以是指向这个对象的另一引用变量。
>>> a = 1000 # 1000 为对象本人 >>> b =a # a为指向1000的引用 >>> a is b True # a,b指向同一对象 >>> a = 1000 >>> b = 1000 # 两个1000为不同的对象 >>> a is b False # a ,b指向不同的对象 >>> a = (1,2,3) >>> b = (1,2,3) # 两个(1,2,3)为不同的对象 >>> a is b False # a ,b指向不同的对象 >>> b = a >>> a is b True # a,b指向同一对象 #list与dict与上列情况相同,略
#然而有趣的是有下列情况: >>> a = 1 >>> b = 1 >>> a is b True >>> a = "asdffasdfffgbf" >>> b = "asdffasdfffgbf" >>> a is b True """ 我们发现尽管是分别赋值,a与b确依然指向同一对象。这是为什么呢? 原来在Python中为了节省存储空间,将0~256之间的重复数字都使用同一存储位置。{而字符串由于出现的没有太多随机性,为了节省内存所以在使用时提前在内存中检查一遍,况且并不会耗费太长时间。}(大括号内为猜想) """
拷贝与赋值的本质区别就是:拷贝是复制(重新开辟一块内存并存储对应数据)一个对象的操作。拷贝只存在标量类型之间。
浅拷贝与深拷贝:
一般情况下浅拷贝与深拷贝是没有区别的,只有当标量类型中出现对某一对象的引用时进行两种拷贝才会出现不同结果,浅拷贝只会复制这个对象的引用,而深拷贝会同时将这个引用的对象copy。例如:
import copy #引入copy模板,进行深拷贝 >>> a = [1,2,3,4] >>> b = ["abc","bcd","cde",a] #b中包含对a(对[1,2,3,4]的引用) >>> c = b.copy() # c为b的浅拷贝 >>> d = copy.deepcopy(b) # d为b的深拷贝 >>> b ['abc', 'bcd', 'cde', [1, 2, 3, 4]] >>> c ['abc', 'bcd', 'cde', [1, 2, 3, 4]] >>> d ['abc', 'bcd', 'cde', [1, 2, 3, 4]] #^^^输出bcd >>> b[3].append(5) # [1,2,3,4].append(5) >>> b ['abc', 'bcd', 'cde', [1, 2, 3, 4, 5]] >>> c ['abc', 'bcd', 'cde', [1, 2, 3, 4, 5]] #c中的数据跟着改变,说明c[3]是对 原[1,2,3,4] 的一个引用 >>> d ['abc', 'bcd', 'cde', [1, 2, 3, 4]] #d中的数据不被影响,说明d[3]是一个不同的[1,2,3,4] >>> b.pop() [1, 2, 3, 4, 5] # b中删除a指向的对象 >>> c ['abc', 'bcd', 'cde', [1, 2, 3, 4, 5]] # c不改变,说明c是一个复制 >>> b.append(6) >>> b ['abc', 'bcd', 'cde', 6] >>> c ['abc', 'bcd', 'cde', [1, 2, 3, 4, 5]] >>> a.append("c[3]是对原来a指向的对象的一个引用") >>> c ['abc', 'bcd', 'cde', [1, 2, 3, 4, 5, 'c[3]是对原来a指向的对象的一个引用']]
用一句话来形容的话,如果被copy的数据是一片森林的话,浅拷贝只会复制所有树的根节点,而深拷贝会复制一片森林。