Python浅拷贝、深拷贝与赋值

首先是python的基本数据类型:

Number(数字) String(字符串) bool(布尔类型) List(列表) Tuple(元组) Set(集合) Dictory(字典)

不可变类型:Number(数字) Tuple(元组) String(字符串) bool(布尔类型)

可变类型:List(列表)Set(集合)Dictory(字典)

可变类型与不可变类型对比

1
2
3
4
a = 45
print(id(a)) # 94458411934400
a = 56
print(id(a)) # 94458411934752
  • 在上述代码中,我们对变量a进行了赋值操作。初始时,变量a引用整数对象45,其内存地址为94458411934400。然后,我们将a赋值为56,此时会新开辟内存空间存储整数对象56,并将变量a重新指向该内存地址,其内存地址变为94458411934752。这是因为不可变类型(如整数)在值发生变化时会创建新的对象,而不是修改原有对象。
1
2
3
4
b = [1, 2, 3]
print(id(b)) # 139680049341440
b.append(5)
print(id(b)) # 139680049341440
  • 在上述代码中,我们创建了一个列表对象b,初始时包含元素[1, 2, 3],其内存地址为139680049341440。然后,我们使用append方法向列表b中添加元素5。由于列表是可变类型,添加元素并不会创建新的对象,而是在原有对象的内存空间上进行修改,因此列表b的内存地址保持不变。

  • 通过以上代码示例,我们可以看出不可变类型在值发生变化时会创建新的对象并修改变量的引用,而可变类型在修改值时会直接在原有对象的内存空间上进行操作,不改变变量的引用。

  • python中的变量声明是动态的,变量的类型是根据赋值自动推断的,不需要显示指定变量的类型。

1
2
3
4
5
6
7
8
9
10
11
12
直接赋值:是对象的引用
a = 10000
# 创建一个整数对象10000,在内存中分配一块空间用于存储它的值,并将创建的对象与变量a相关联,变量a引用了该对象
b = a
# 变量b被赋值为变量a,b引用了与变量a相同的地址,指向同一个整数对象
c = 10000
# 变量c被赋值为10000,又创建了一个新的整数对象,并将变量c与该对象相关联
print(id(a)) # 139680049769968
print(id(b)) # 139680049769968
# 变量a和变量b指向相同的对象,它们的内存地址是相同的
print(id(c)) # 139680049766704
# 变量c指向新的对象,因此它的内存地址与变量a和变量b不同
  • 在上述代码中,变量a、b和c都被赋值为整数对象10000,但是变量a和变量b指向同一个对象,它们的内存地址是相同的,而变量c则指向一个新的对象,因此其内存地址与变量a和变量b不同。

  • 需要注意的是,变量在赋值时实际上是引用了对象,而不是直接存储对象的值。这意味着变量可以引用不同类型的对象,因为类型是根据对象自动推断的。

浅拷贝与深拷贝

在Python中,我们可以使用浅拷贝和深拷贝来复制对象,Python中的拷贝操作可以使用copy模块中的copy函数进行浅拷贝,使用copy模块中的deepcopy函数进行深拷贝。它们的区别在于:

  • 浅拷贝:创建一个新的对象,然后将原始对象的引用复制给新对象,这样原始对象和新对象将引用同一个内存地址,它们指向相同的对象。只拷贝父对象本身,不会拷贝父对象的子对象

  • 深拷贝:创建一个新对象,并递归的复制原始对象及子对象。深拷贝会创建一个独立的对象,即新对象和原始对象是独立的,它们拥有不同的内存地址。深拷贝会完全拷贝父对象及子对象

1
2
3
4
5
6
7
8
9
#浅拷贝与深拷贝
import copy
a=[100,2,3]
b=copy.copy(a)
c=copy.deepcopy(b)
a.append(5)
print(id(a))#139679863695488
print(id(b))#139679863460160
print(id(c))#139679863461632

观察输出结果可以发现:

  • 列表a发生了变化,添加了一个新的元素5。
  • 列表b保持不变,它仍然是原始的浅拷贝,因此不会受到a的修改的影响。
  • 列表c也保持不变,它是原始的深拷贝,因此不会受到a的修改的影响。
  • 打印出的内存地址(id)可以看到,abc指向的是不同的内存空间,它们是相互独立的对象。

这个例子展示了浅拷贝和深拷贝的不同行为。浅拷贝只复制了对象的引用,而深拷贝递归地复制了整个对象及其子对象。因此,在修改原始对象时,浅拷贝不会受到影响,而深拷贝则是完全独立的副本。

1
2
3
4
5
import copy
a = [[100, 4, 8], 2, 3]
b = copy.copy(a)
c = copy.deepcopy(b)
a[0].append(5)

输出结果如下:

1
2
3
4
5
6
[[100, 4, 8, 5], 2, 3]
[[100, 4, 8, 5], 2, 3]
[[100, 4, 8], 2, 3]
139679893614720
139679893614720
139679893614592

从输出结果可以观察到以下情况:

  • 列表 a 的第一个元素是一个嵌套列表 [100, 4, 8],在进行浅拷贝后,列表 b 也引用了相同的嵌套列表对象。因此,对 a[0] 进行修改会影响到 b[0],导致它们的值都增加了 5
  • 使用深拷贝 copy.deepcopy() 创建的列表 c,在进行修改时并不会影响原始的列表 a 和浅拷贝的列表 b。因此,c[0] 的值保持不变。
  • 尽管 a[0]b[0] 的值相同,但它们的内存地址是不同的。

综上所述,浅拷贝只复制了列表对象的引用,因此对可变对象的修改会影响到原始对象和浅拷贝对象。而深拷贝创建了原始对象及其所有嵌套对象的独立副本,因此对副本的修改不会影响到原始对象。此外,内存地址的不同也说明了它们是不同的对象。


:D 一言句子获取中...