python的赋值、浅层拷贝和深层拷贝解释

2024-01-16 10:56:11 浏览数 (2)

或许你在其他编程语言比如C 有听说过拷贝分深拷贝和浅拷贝。这两个概念区别就是你复制的是一份对象的引用还是对象本身。今天我们来看一下python的赋值、浅层拷贝和深层拷贝的区别。

一、赋值和拷贝

开始之前,先看一段代码。

代码语言:python代码运行次数:0复制
import copy
arr = [ [1, 2, 3], [4, 5, 6] ]

assigned = arr  #assign
swallow_copied = copy.copy(arr)  #swallow copy
deep_copied = copy.deepcopy(arr)  # deep copy

这里看到了我们声明了一个二维数组arr,并使用这个arr赋值了assigned、浅拷贝了swallow_copied和深拷贝了deep_copied。

1.1 对象的内存布局

我们使用python tour来看下三者在object层是如何布置的。

很清楚看到 :

  • arr的object对象的引用 赋值给了assigned,也就是assigned的object是等价于arr
  • swallow_copied复制了第一层的对象[[][]], 但是就最终的object[1,2,3]和[4,5,6]和arr并无区别
  • deep_copied则是在object层完整地拷贝了一份新的object。和arr是完全两码事。

1.2 修改object

接上段代码,接着来修改object。加入了在swallow_copied和deep_copied的操作代码,并和原来的arr进行比较。

代码语言:python代码运行次数:3复制
import copy
arr = [ [1, 2, 3], [4, 5, 6] ]

assigned = arr  #assign
swallow_copied = copy.copy(arr)  #swallow copy
deep_copied = copy.deepcopy(arr)  # deep copy


print("before:")
print("arr:", arr)
print("swallow_copied:", swallow_copied)
print("deep_copied:", deep_copied)

swallow_copied.append([7, 8, 9])
swallow_copied[0].append(3.5)

print()
print("after swallow copy:")
print("arr:", arr)
print("swallow_copied:", swallow_copied)

deep_copied.append([7, 8, 9])
deep_copied[0].append(3.5)

print()
print("after deep copy:")
print("arr:", arr)
print("deep_copied:", deep_copied)

使用python tour观察这段代码,你看到区别了吗?

一开始我们的数组长这样:

before:

arr: [ [1, 2, 3], [4,5,6] ]

swallow_copied: [ [1,2,3], [4,5,6] ]

deep_copied:[ [1,2,3], [4,5,6] ]

经过这两行代码进行浅层拷贝之后

代码语言:javascript复制
swallow_copied.append([7, 8, 9])
swallow_copied[0].append(3.5)

swallow_copied变成

after swallow copy:

arr: [ [1, 2, 3, 3.5], [4,5,6] ]

swallow_copied: [ [1, 2, 3, 3.5], [4, 5, 6], [7, 8, 9]]

可以看到虽然我只是操作swallow_copied,但是由于swallow_copied和arr共享了swallow_copied[0]的对象,所以这里arr[0]也跟着改变。

然后再经过这两行代码进行深层拷贝之后

代码语言:python代码运行次数:0复制
deep_copied.append([7, 8, 9])
deep_copied[0].append(3.5)

deep_copied变成

after deep copy:

arr: [ [1, 2, 3, 3.5], [4,5,6] ]

deep_copied: [ [1, 2, 3, 3.5], [4, 5, 6], [7, 8, 9]]

可以看到操作deep_copied并没有改变arr。

二、mutable和immutable

我们来看下python的赋值copy和deepcopy操作,以及对mutable和immutable类型操作的区别。

先来看一段代码

代码语言:python代码运行次数:1复制
x = 5
print(x)

y=x
print(y)

x  = 1
print(x)
print(y)

从这段代码的执行结果来看,x =1并没有改变y。也就是说虽然y=x,但是x和y并不是同一个object。

2.1 mutable和非mutable类型

python把object分为两种类型:

  • 一种叫immutable,包括integer,float和string。
  • 另一种叫mutable,包括list,dict等容器。

python对不同类型也有不同的处理方式

  • 这里y=x,也就是说每次处理immutable类型,都会真真实实地创建新的object
  • 而处理mutable类型,object还是那个object,只是object里面的values被改变了。你操作了这个object的reference。

2.2 浅拷贝和深拷贝的区别

浅层拷贝和深拷贝只有在操作immutable对象时有区别。

  • mutable对象如果持有的是immutable的对象,比如说[1,2,3]这里mutable的[]持有的是immutable的1,2,3。浅层拷贝和深拷贝[1,2,3]是没有区别的
  • 如果mutable对象持有的mutable对象。比如说[[1,2,3], [4,5,6]]这里mutable的[[], []] 持有的是mutable的[]。那么mutable的浅层拷贝创建了第一层级的对象,里面的内嵌对象只是保留了reference。但是深拷贝除了会创建第一层级的对象之外,里面的内嵌对象也会创建新的对象。

0 人点赞