深拷贝、浅拷贝、直接复制的区别
不妨举一个例子,有一个名为Date
的类和一个名为Time
的类,如下定义:
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return f"{self.year}-{self.month}-{self.day}"
class Time:
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
def __str__(self):
return f"{self.hour}:{self.minute}:{self.second}"
现在我们再定义一个名为Datetime
的类:
class Datetime:
def __init__(self, date, time, tag: str):
self.date = date
self.time = time
self.tag = tag
def __str__(self):
return f"{self.tag} {self.date} {self.time}"
现在我们创建一个Datetime
对象:
dt = Datetime(Date(2024, 1, 1), Time(0, 0, 0), "example1")
接下来考虑:如何复制这个对象?
直接赋值
最容易想到的方法就是直接赋值,如下:
dt2 = dt
此时我们对dt2进行一下修改,将它的tag
改成"example2",看一下会发生什么:
dt2.tag = "example2"
print(dt)
print(dt2)
输出:
example2 2024-1-1 0:0:0
example2 2024-1-1 0:0:0
可以看到这两个对象的tag
属性都已经被修改为了"example2",这是因为直接赋值时,Python会把dt1
和dt2
这两个名字指向同一个对象。
浅拷贝
浅拷贝可以用copy.copy()
实现,修改上述代码:
from copy import copy
dt = Datetime(Date(2024, 1, 1), Time(0, 0, 0), "example1")
dt2 = copy(dt)
dt2.tag = "example2"
print(dt)
print(dt2)
输出:
example1 2024-1-1 0:0:0
example2 2024-1-1 0:0:0
可以看到,dt2
的tag
属性被修改了,但是dt
的tag
属性没有被修改。但是当我们将d2修改为2023年1月1日:
dt2.date.year = 2023
print(dt)
print(dt2)
输出:
example1 2023-1-1 0:0:0
example2 2023-1-1 0:0:0
可以看到,dt
的date
属性也被修改了,这是因为浅拷贝不会处理更深层的属性。这时我们可以用深拷贝:
深拷贝
深拷贝可以用copy.deepcopy()
实现,修改上述代码:
from copy import deepcopy
dt = Datetime(Date(2024, 1, 1), Time(0, 0, 0), "example1")
dt2 = deepcopy(dt)
dt2.tag = "example2"
dt2.date.year = 2023
print(dt)
print(dt2)
输出:
example1 2024-1-1 0:0:0
example1 2023-1-1 0:0:0
可以看到,dt2
的date
属性没有被修改,这是因为深拷贝会递归地处理更深层的属性。深拷贝下的dt
和dt2
可以看作完全无关。