添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1、Python 深拷贝和浅拷贝概念理解

  • 浅拷贝,指的是 重新分配一块内存 创建一个新的对象 ,但里面的元素是 原对象中各个子对象的引用

  • 深拷贝,是指 重新分配一块内存 ,创建一个新的对象,并且将原对象中的元素,以递归的方式, 通过创建新的子对象拷贝到新对象中 。因此, 新对象和原对象没有任何关联

2、浅拷贝

  • 使用数据类型 本身的构造器
  • 对于 可变的序列 ,还可以通过切片操作符 : 来完成浅拷贝
  • Python 还提供了对应的函数 copy.copy() 函数,适用于任何数据类型

2.1 使用数据类型本身的构造器

list1 = [1, 2, 3]
list2 = list(list1)
print(list2)
print("list1==list2 ?",list1==list2)
print("list1 is list2 ?",list1 is list2)
set1= set([1, 2, 3])
set2 = set(set1)
print(set2)
print("set1==set2 ?",set1==set2)
print("set1 is set2 ?",set1 is set2)
dict1 = {1:[1,'w'], 2:0, 3:98}
dict2 = dict(dict1)
print(dict2)
print("dict1 == dict2 ?",dict1 == dict2)
print("dict1 is dict2 ?",dict1 is dict2)
[1, 2, 3]
list1==list2 ? True
list1 is list2 ? False
{1, 2, 3}
set1==set2 ? True
set1 is set2 ? False
{1: [1, 'w'], 2: 0, 3: 98}
dict1 == dict2 ? True
dict1 is dict2 ? False

分析: 浅拷贝,为新变量重新分配一块内存,和原来变量的内存不一样,所以有

list1 is list2 ? False
set1 is set2 ? False
dict1 is dict2 ? False

但浅拷贝完,两个变量中的元素的值是一样的。

list1==list2 ? True
dict1 == dict2 ? True
set1==set2 ? True

2.2 对于列表

对于列表,还可以通过切片操作符“:”来完成浅拷贝

list1 = [1, 2, 3]
list2 = list1[:]
print(list2)
print("list1 == list2 ?",list1 == list2)
print("list1 is list2 ?",list1 is list2)
[1, 2, 3]
list1 == list2 ? True
list1 is list2 ? False

2.3 使用 copy.copy() 函数

函数 copy.copy() 函数,适用于任何数据类型

import copy
list1 = [1, 2, 3]
list2 = copy.copy(list1)
print(list2)
print("list1 == list2 ?",list1 == list2)
print("list1 is list2 ?",list1 is list2)
set1 = {1, 2, 3}
set2 = copy.copy(set1)
print(set2)
print("set1 == set2 ?",set1 == set2)
print("set1 is set2 ?",set1 is set2)
dict1 = {1:'xiaoming', 2:'xiahua',3:'xiaoli'}
dict2 = dict(dict1)
print(dict2)
print("dict1 == dict2 ?",dict1 == dict2)
print("dict1 is dict2 ?",dict1 is dict2)
[1, 2, 3]
list1 == list2 ? True
list1 is list2 ? False
{1, 2, 3}
set1 == set2 ? True
set1 is set2 ? False
{1: 'xiaoming', 2: 'xiahua', 3: 'xiaoli'}
dict1 == dict2 ? True
dict1 is dict2 ? False

2.4 对于元组

对于元组,使用 tuple() 或者切片操作符 ‘:’ 不会创建一份浅拷贝,相反它会返回一个指向相同元组的引用:

tuple1 = (1, 2, 3)
tuple2 = tuple(tuple1)
print(tuple2)
print("tuple1 == tuple2 ?",tuple1 == tuple2)
print("tuple1 is tuple2 ?",tuple1 is tuple2)
tuple1 = (1, 2, 3)
tuple2 = tuple1[:]
print(tuple2)
print("tuple1 == tuple2 ?",tuple1 == tuple2)
print("tuple1 is tuple2 ?",tuple1 is tuple2)
(1, 2, 3)
tuple1 == tuple2 ? True
tuple1 is tuple2 ? True
(1, 2, 3)
tuple1 == tuple2 ? True
tuple1 is tuple2 ? True

使用 tuple() 或者切片操作符 ‘:’ 不会创建一份浅拷贝,因为它开辟新的内存存储的是原对象的引用,而没有创建新的对象来存储原对象的子对象的引用,所以不是浅拷贝。相反它会返回一个指向相同元组的引用。

对字符串使用 str() 或者切片操作符 ‘:’,原理和 元组相同。

str1 = 'operation'
str2 = str1[:]
print(str2)
print("str1 == str2 ?",str1 == str2)
print("str1 is str2 ?",str1 is str2)
operation
str1 == str2 ? True
str1 is str2 ? True
str1 = 'operation'
str2 = str(str1)
print(str2)
print("str1 == str2 ?",str1 == str2)
print("str1 is str2 ?",str1 is str2)
operation
str1 == str2 ? True
str1 is str2 ? True
import copy
set1 = (1, 2, 3)
set2 = copy.copy(set1)
print(set2)
print("set1 == set2 ?",set1 == set2)
print("set1 is set2 ?",set1 is set2)
(1, 2, 3)
set1 == set2 ? True
set1 is set2 ? True
import copy
set1 = 'operation'
set2 = copy.copy(set1)
print(set2)
print("set1 == set2 ?",set1 == set2)
print("set1 is set2 ?",set1 is set2)
operation
set1 == set2 ? True
set1 is set2 ? True

也就是说,对字符串和元组使用 copy()、[:]、本身的构造器完成的复制,都只是开辟了内存存储原对象的引用,而不是存储原对象的子对象的引用。

2.5 关于切片操作符 ‘:’

切片操作符 ‘:’ 不能用于字典和集合完成浅拷贝

# set1 = {1, 2, 3}
# set2 = set1[:]
# dict1 = {1:1, 2:2, 3:3}
# dict2 = dict1[:]

2.6 和赋值的区别

和赋值的本质区别在于,赋值只是把原对象的引用给到新对象

set1 = {1:1, 2:2, 3:3}
set2 = set1
print(set2)
print("set1 == set2 ?",set1 == set2)
print(id(set1))
print(id(set2))
{1: 1, 2: 2, 3: 3}
set1 == set2 ? True
2515252654368
2515252654368

2.7 浅拷贝需注意的问题

对数据采用浅拷贝的方式时,如果原对象中的元素不可变,那倒无所谓;但如果元素可变,浅拷贝通常会出现一些问题,

list1 = [[1, 2], (30, 40)]
list2 = list(list1)
list1.append(100)
print("list1:",list1)
print("list2:",list2)
list1[0].append(3)
print("list1:",list1)
print("list2:",list2)
list1[1] += (50, 60)
print("list1:",list1)
print("list2:",list2)
list1: [[1, 2], (30, 40), 100]
list2: [[1, 2], (30, 40)]
list1: [[1, 2, 3], (30, 40), 100]
list2: [[1, 2, 3], (30, 40)]
list1: [[1, 2, 3], (30, 40, 50, 60), 100]
list2: [[1, 2, 3], (30, 40)]

2、深拷贝

Python 中以 copy.deepcopy() 来实现对象的深度拷贝

import copy
list1 = [[1, 2], (30, 40)]
list2 = copy.deepcopy(list1)
list1.append(100)
print("list1:",list1)
print("list2:",list2)
list1[0].append(3)
print("list1:",list1)
print("list2:",list2)
list1[1] += (50, 60)
print("list1:",list1)
print("list2:",list2)
list1: [[1, 2], (30, 40), 100]
list2: [[1, 2], (30, 40)]
list1: [[1, 2, 3], (30, 40), 100]
list2: [[1, 2], (30, 40)]
list1: [[1, 2, 3], (30, 40, 50, 60), 100]
list2: [[1, 2], (30, 40)]
                                    浅拷贝创建一个新的复合对象,然后(通过引用)将原始对象中的子对象放入新对象中。这意味着,如果原始对象中的某个元素是可变的(如列表、字典等),那么拷贝后的新对象中的相应元素仍然是原始元素的引用,而非其独立副本。因此,对于这些共享的子对象所做的任何非就地(non-in-place)修改都会反映到所有引用这些子对象的对象中。深拷贝浅拷贝相反,它不仅创建了一个新的复合对象,而且递归地创建了原始对象中所有子对象的副本。因此,深拷贝后的对象与原始对象完全独立,对任一对象的修改都不会影响到另一个。
                                    浅拷贝:复制对象,但嵌套对象仍然引用原对象中的子对象。深拷贝:复制对象,包括所有嵌套的子对象,使得新对象与原对象完全独立。深拷贝开发过程中主要用于防止副作用、处理复杂嵌套结构、避免多线程环境中的数据竞争以及实现历史记录和撤销功能。根据具体需求和场景选择使用深拷贝可以提高代码的健壮性和可维护性。
                                    (4)举例:例如以上的列表list1中,有[1, 2]、(30, 40)、'kkk'三个元素,首先列表本身list1是可变的类型,元素[1, 2]是可变的,元素(30, 40)和'kkk'是不可变的。(1)对象:对象有存储地址id【就好像某个地址的快递仓库】,对象中的元素都有一个地址id【就像仓库的某某货架】(2)对象与对象的元素:(对象)或(对象中的元素)有不同的类型【数字,字符串,列表,字典,集合,元组】定义:可变就是增删改查之后,对象或元素的存储id不会发生改变,反之就不可变。
                                    由于新对象与原始对象并无共享内存地址,故而二者完全独立,因此更改其中一个对象的值并不会影响另一个对象的值。浅复制是指新建一个对象,然后将原始对象的引用复制给新对象。由于新对象与原始对象同一内存地址,因此一个对象的值被修改后,另一个对象的值也会受到影响。深拷贝浅拷贝Python中是非常重要的存在,但很多人对它们了解的并不是很清楚,本文为大家详细讲解一下浅拷贝的概念、使用场景以及注意事项,希望能够给你带来帮助。在使用深拷贝时,如果对象的层次结构比较复杂,可能会导致性能问题,因此必须小心使用深拷贝。
常规拷贝在原始变量 x 的改变后,因为共用同一个内存地址,因此会直接放到被复制的变量 y 上,导致“不知情”的情况下导致 y 变量在没有操作的情况下改变。
解决办法就是使用浅拷贝
浅拷贝会将两个变量分别放在不同的内存地址,解决了常规拷贝的缺点。
但是,对于字典或列表中有嵌套的情况,浅拷贝同样不会生效。
这时候就需要用的深拷贝。
可以看到,深拷贝可以解决
                                    除ID之外,其他状态都有可能发生改变可变对象有:列表、集合、字典l = []print("修改前id= ", id(l))print("修改后id= ", id(l))print(l)结果:修改前id= 2597667097216修改后id= 2597667097216[132]2、什么是不可变对象,有哪些?包括id在内的各种状态,都不会发生变化不可变对象有:大部分是python内置数据类型: 数字,字符串,元组i = 123print("修改前id= ", id(i))
                                    copy函数是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象。对于不可变对象而言,浅拷贝只是引用赋值。deepcopy函数是深拷贝,对于可变类型,深拷贝是逐层进行拷贝。对于不可变类型而言,也是引用赋值。# 对于不可变对象来说,无论浅拷贝 都不会进行拷贝,只是引用赋值。# 对于可变对象,浅拷贝只拷贝第一层数据,深拷贝会逐层拷贝。可变对象中,保存的可变数据。可变对象中保存不可变数据。