Python赋值是编程过程中的一项基本操作。在Python中,赋值可分为深度拷贝和浅度拷贝两种类型。本文将详细介绍Python中的深度拷贝和浅度拷贝,并通过代码展示它们之间的区别和使用情况。
1. 赋值操作简介
在Python中,赋值是一种操作符。它可以将某一个对象赋值给一个变量或者一个表达式,例如:a=5。在此例子中,5被赋值给变量a。赋值操作可以通过以下方式实现:
a = 5
b = 'hello'
c = [1, 2, 3]
1.1. 不可变类型和可变类型
Python中的对象可以分为两种类型:不可变类型和可变类型。不可变类型是指一旦对象被创建,它们的值就无法更改,例如字符串和元组。相反,可变类型是指对象的值可以更改,例如列表和字典。
以下是Python的一些不可变类型:
a = 5 #整数
b = 3.1415 #浮点数
c = True #布尔型
d = 'hello' #字符串
e = (1, 2, 3) #元组
以下是Python的一些可变类型:
a = [1, 2, 3] #列表
b = {'name': 'Jerry', 'age': 20} #字典
2. 浅拷贝(Shallow Copy)
浅拷贝是在Python中复制对象的一种方式。它创建了一个新对象,其中包含了原始对象的引用。新对象和原始对象共享一个内存地址。如果原始对象发生更改,它的所有浅拷贝都会反映这些更改。
下面我们来看一个浅拷贝的例子:
#浅拷贝列表
a = [1, 2, 3]
b = a.copy()
print(b) #[1, 2, 3]
#修改原始列表
a[0] = 'hello'
print(a) #['hello', 2, 3]
#浅拷贝后的列表也被修改了
print(b) #[1, 2, 3]
在这个例子中,我们使用了列表的copy()方法进行浅拷贝操作。我们更改了原始列表a的第一个索引,然后检查了浅拷贝后的b列表。由于b只是a的引用,所以b的内容也被更改了。
因为浅拷贝共享原始对象的内存地址,如果原始对象是可变类型,当它被更改时,所有的浅拷贝都会受到影响。
2.1. 浅拷贝的方式
Python提供了三种浅拷贝的方式:
2.1.1. 切片操作
切片操作[:]是Python中最简单的浅拷贝方法。它可以创建一个新的对象,其中包含原始对象的所有元素。
以下是一个使用切片操作进行浅拷贝的例子:
a = [1, 2, 3]
b = a[:]
print(b) #[1, 2, 3]
#修改原始列表
a[0] = 'hello'
print(a) #['hello', 2, 3]
#浅拷贝后的列表没有被修改
print(b) #[1, 2, 3]
在这个例子中,我们使用了切片操作来浅拷贝列表。当我们更改原始列表a的第一项时,浅拷贝后的b列表并没有受到影响。这是因为切片操作创建了一个新的列表,而不是一个指向原始列表的引用。
2.1.2. 工厂函数
Python提供了一些工厂函数,例如list()和dict()。这些函数可以用于浅拷贝对象。
以下是一个使用list()方法浅拷贝的例子:
a = [1, 2, 3]
b = list(a)
print(b) #[1, 2, 3]
#修改原始列表
a[0] = 'hello'
print(a) #['hello', 2, 3]
#浅拷贝后的列表没有被修改
print(b) #[1, 2, 3]
在这个例子中,我们使用了list()方法来浅拷贝列表a。与切片操作类似,这创建了一个新的列表。
2.1.3. copy()方法
大多数Python对象都有一个名为copy()的方法。这个方法可以用于浅拷贝对象。
以下是一个使用copy()方法浅拷贝的例子:
a = [1, 2, 3]
b = a.copy()
print(b) #[1, 2, 3]
#修改原始列表
a[0] = 'hello'
print(a) #['hello', 2, 3]
#浅拷贝后的列表没有被修改
print(b) #[1, 2, 3]
在这个例子中,我们使用了copy()方法来浅拷贝列表a。
2.2. 浅拷贝的应用场景
浅拷贝最常见于对元组、列表和字典等可变对象的复制,以防止在修改原始对象时意外更改其他对象。但是,如果原始对象包含不可变对象,则浅拷贝非常有用。在这种情况下,浅拷贝将复制对象的引用,这可以节省内存。
3. 深拷贝(Deep Copy)
深拷贝创建一个新的对象,其中包含原始对象及其引用的新副本。新对象和原始对象不共享内存地址。如果原始对象发生更改,深拷贝不会反映这些更改。
下面我们来看一个深拷贝的例子:
import copy
#深拷贝列表
a = [1, 2, [3, 4]]
b = copy.deepcopy(a)
print(b) #[1, 2, [3, 4]]
#修改原始列表
a[2][0] = 'hello'
print(a) #[1, 2, ['hello', 4]]
#深拷贝后的列表没有被修改
print(b) #[1, 2, [3, 4]]
在这个例子中,我们使用了copy库中的deepcopy()方法。我们使用它来深拷贝包含未嵌套列表的列表,并检查更改是否影响到了深拷贝后的列表b。在这个例子中,更改了原始列表a中嵌套列表的一个元素,但是深拷贝后的列表b并未受到影响。
3.1. copy()方法进行深拷贝
大多数Python对象都有一个名为copy()的方法。这个方法可以用于深拷贝对象,但仅限于嵌套列表或字典中,对于其他对象的深拷贝则无效。为了深拷贝其他数据类型的对象,应使用copy库中的deepcopy()方法。
以下是一个使用copy()方法进行深拷贝的例子:
import copy
#深拷贝列表
a = [1, 2, [3, 4]]
b = a.copy()
print(b) #[1, 2, [3, 4]]
#修改原始列表
a[2][0] = 'hello'
print(a) #[1, 2, ['hello', 4]]
#浅拷贝后的列表也被修改了
print(b) #[1, 2, ['hello', 4]]
在这个例子中,我们使用了列表的copy()方法来进行深拷贝操作。与浅拷贝不同,深拷贝可以创建一个新的对象,其中包含原始对象及其引用的副本。
3.2. 深度拷贝的应用场景
深拷贝最常用于拷贝嵌套对象,对于嵌套的列表和字典这点特别有效。它也很有用的防止在执行某些算法和递归函数时发生意外更改,因为它可以保证新对象与原始对象完全独立。
4. 总结
浅拷贝和深拷贝都是Python中非常重要的赋值操作。它们各自有不同的使用场景和特点。浅拷贝包含原始对象的引用,而深拷贝包含原始对象及其引用的新副本。深拷贝适用于任何数据类型,浅拷贝通常用于可变类型。对于嵌套对象,深拷贝在防止意外修改方面非常有用。无论何时进行拷贝操作,请始终考虑实际使用情况,以确保代码的正确性。
最后让我们来看一段代码,通过更改temperature值来观察深浅拷贝的效果。
先定义一个共享引用的变量:
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a)
c = copy.deepcopy(a)
更改temperature值为0.6:
temperature = 0.6
浅度拷贝,更改a[2][0]:
a[2][0] = 'hello'
print(b)
结果是,b中也有['hello',4]。
使用深度拷贝,更改a[2][0]:
a[2][0] = 'world'
print(c)
结果是,c中还是[3, 4]。
因为深度拷贝会把嵌套的所有对象都复制一份,在不同地址中创建一个全新的相同结构的对象。但是浅拷贝只是创建了一个新的引用,这个新的引用指向了原始对象的内存地址。如果原始对象中包含嵌套对象,浅拷贝会将嵌套对象的引用复制到新对象中。这就是为什么浅拷贝的嵌套对象仍然会共享一个内存地址。