首先,我们知道Python3中,有6个标准的数据类型,他们又分为可变和不可变,我来为大家讲解一下关于python怎么浅拷贝和深拷贝?跟着小编一起来看一看吧!

python怎么浅拷贝和深拷贝(python3浅拷贝与深拷贝的区别和理解)

python怎么浅拷贝和深拷贝

首先,我们知道Python3中,有6个标准的数据类型,他们又分为可变和不可变。

不可变数据(3个):

  1. Number(数字)
  2. String(字符串)
  3. Tuple(元组)

可变数据(3个):

  1. List(列表)
  2. Dictionary(字典)
  3. Set(集合)
浅拷贝
  1. 对于 不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间。
  2. 对于 可 变类型 List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的),进行浅拷贝
  3. 浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;改变原始对象中为不可变类型的元素的值,只有原始类型受影响。 (操作拷贝对象对原始对象的也是同理)

可变类型和不可变类型在浅拷贝中的区别

import copy # 不可变类型 Number String Tuple print("对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间拷贝值") num1 = 17 num2 = copy.copy(num1) print("num1:" str(id(num1))) print("num2:" str(id(num1))) # num1和num2的地址都相同 str1 = "hello" str2 = copy.copy(str1) print("str1:" str(id(str1))) print("str2:" str(id(str2))) # str1和str2的地址都相同 tup1 = (18, "tom") tup2 = copy.copy(tup1) print("tup1:" str(id(tup1))) print("tup2:" str(id(tup2))) # tup1和tup2的地址都相同 print("="*20) print("对于可变类型 List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间),进行浅拷贝") list1 = [11,12] list2 = copy.copy(list1) print("list1:" str(id(list1))) print("list2:" str(id(list2))) # list1和list2的地址不相同 dic1 = [11,12,"hi"] dic2 = copy.copy(dic1) print("dic1:" str(id(dic1))) print("dic2:" str(id(dic2))) # dic1和dic2的地址不相同 set1 = {"AA","BB"} set2 = copy.copy(set1) print("set1:" str(id(set1))) print("set2:" str(id(set2))) # set1和set2的地址不相同

结果:

对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间拷贝值 num1:4449693616 num2:4449693616 str1:4452098488 str2:4452098488 tup1:4451942472 tup2:4451942472 ==================== 对于可变类型 List、Dictionary、Set,浅复制会开辟新的空间地址,进行浅拷贝 list1:4456844424 list2:4452360136 dic1:4452358856 dic2:4456844744 set1:4452279016 set2:4452279464

对list进浅拷贝,对可变类型和不可变类型修改后的影响


import copy l1 = [11, 12] l2 = [21, 22] num = 555 allOne = [l1, l2,num] # 浅拷贝,创建出一个对象,并把旧对象元素的 引用地址 拷贝到新对象当中。 # 也就是说,两个对象里面的元素通过浅拷贝指向的还是同一个地址 allOne2 = copy.copy(allOne) l1[0] = 16 # 此处修改,会使得 allOne 和 allOne2的第0个元素的值都发生改变,因为l1是List,是可变对象 allOne[2] = 666 # 此处修改,只会allOne的num的值,因为不可变对象一旦重新复制,地址就会发生改变。(不可变嘛) num = 777 # 此处不会改变 allOne 和 allOne2的值,因为相当于 777 复制给一个全新的地址,这个num跟其他num已经没关系了 print(allOne) print(allOne2) print("id allOne:" str(id(allOne))) print("id allOne[0]:" str(id(allOne[0]))) print("id allOne[1]:" str(id(allOne[1]))) print("id allOne[2]:" str(id(allOne[2]))) print("===") print("id allOne2:" str(id(allOne2))) print("id allOne2[0]:" str(id(allOne2[0]))) print("id allOne2[1]:" str(id(allOne2[1]))) print("id allOne2[2]:" str(id(allOne2[2])))

结果:

[[16, 12], [21, 22], 666] [[16, 12], [21, 22], 555] id allOne:4467341640 id allOne[0]:4471819912 id allOne[1]:4467342920 id allOne[2]:4466847696 === id allOne2:4471820232 id allOne2[0]:4471819912 id allOne2[1]:4467342920 id allOne2[2]:4466081744


结论:

  1. 改动allOne中的可变类型,会影响allOne2,改变allOne2同理影响allOne。
  2. 改动allOne2中的不可变类型,只有allOne2自身会改变,allOne不受影响。

备注:List是可变类型

对于不可变类型被修改后造成的影响,我们用一个更加简单的例子便可更好理解:

num = 123 print(str(id(num))) num = 666 print(str(id(num)))

结果:

4348603632 4350009296

深拷贝
  1. 浅拷贝,除了顶层拷贝,还对子元素也进行了拷贝(本质上递归浅拷贝)
  2. 经过深拷贝后,原始对象和拷贝对象所有的子元素地址都是独立的了
  3. 可以用分片表达式进行深拷贝
  4. 字典的copy方法可以拷贝一个字典

深拷贝对6种基本类型的影响

我们对3种可变类型3种不可变类型进行深拷贝。
结果发现,和浅拷贝几乎一致。

其实这也好理解,因为的深拷贝对比浅拷贝,强调的是 递归,强调的是资源素。
对了顶层的操作,深浅拷贝无异。

import copy # 不可变类型 Number String Tuple print("对于不可 变类型 Number String Tuple,深复制依然是地址指向,不会开辟新空间拷贝值") num1 = 17 num2 = copy.deepcopy(num1) # 深拷贝 print("num1:" str(id(num1))) print("num2:" str(id(num1))) # num1和num2的地址都相同 str1 = "hello" str2 = copy.deepcopy(str1) # 深拷贝 print("str1:" str(id(str1))) print("str2:" str(id(str2))) # str1和str2的地址都相同 tup1 = (18, "tom") tup2 = copy.deepcopy(tup1) # 深拷贝 print("tup1:" str(id(tup1))) print("tup2:" str(id(tup2))) # tup1和tup2的地址都相同 print("="*20) print("对于可变类型 List、Dictionary、Set,深拷贝会开辟新的空间地址,进行拷贝") list1 = [11,12] list2 = copy.deepcopy(list1) # 深拷贝 print("list1:" str(id(list1))) print("list2:" str(id(list2))) # list1和list2的地址不相同 dic1 = [11,12,"hi"] dic2 = copy.deepcopy(dic1) # 深拷贝 print("dic1:" str(id(dic1))) print("dic2:" str(id(dic2))) # dic1和dic2的地址不相同 set1 = {"AA","BB"} set2 = copy.deepcopy(set1) # 深拷贝 print("set1:" str(id(set1))) print("set2:" str(id(set2))) # set1和set2的地址不相同

import copy l1 = [11, 12] l2 = [21, 22] num = 555 allOne = [l1, l2,num] # 浅拷贝,除了顶层拷贝,还对子元素也进行了拷贝(本质上递归浅拷贝) # 经过深拷贝后,原始对象和拷贝对象所有的元素地址都没有相同的了 allOne2 = copy.deepcopy(allOne) # copy.deepcopy 深拷贝 allOne[1] = [113,114] allOne2[2] = [227,228] print(allOne) print(allOne2) print("id allOne:" str(id(allOne))) print("id allOne[0]:" str(id(allOne[0]))) print("id allOne[1]:" str(id(allOne[1]))) print("id allOne[2]:" str(id(allOne[2]))) print("===") print("id allOne2:" str(id(allOne2))) print("id allOne2[0]:" str(id(allOne2[0]))) print("id allOne2[1]:" str(id(allOne2[1]))) print("id allOne2[2]:" str(id(allOne2[2])))

结果:

[[11, 12], [113, 114], 555] [[11, 12], [21, 22], [227, 228]] id allOne:4549589640 id allOne[0]:4554067720 id allOne[1]:4554067848 id allOne[2]:4548329424 === id allOne2:4554067912 id allOne2[0]:4554067784 id allOne2[1]:4554067592 id allOne2[2]:4554100808

其他拷贝方式

除了copy模块的中的copy和deepcopy,还有其他自带的方式可实现拷贝。

分片表达式拷贝

l1 = [11, 12] l2 = [21, 22] num = 555 orgi = [l1, l2, num] nList = orgi[:] print("orgi:" str(id(orgi))) print("orgi[0]:" str(id(orgi[0]))) print("orgi[1]:" str(id(orgi[1]))) print("orgi[2]:" str(id(orgi[2]))) print("*"*30) print("nList:" str(id(nList))) print("nList[0]:" str(id(nList[0]))) print("nList[1]:" str(id(nList[1]))) print("nList[2]:" str(id(nList[2])))

用分片表达式进行的拷贝,是浅拷贝。

字典自带的copy方法可实现深拷贝

dic = {"key": "hello", "num": 18} dic2 = dic.copy() dic["key"] = "one" dic2["key"] = "two" print(dic) print("dic:" str(id(dic))) print(dic2) print("dic2:" str(id(dic2)))

结果:

{'key': 'one', 'num': 18} dic:4382946792 {'key': 'two', 'num': 18} dic2:4382946864

,