Python列表赋值、浅拷贝与深拷贝的区别详解
本文详细解析Python中列表的三种处理方式:直接赋值、浅拷贝与深拷贝。通过对比[[wp:a1]]内存地址与内容变化,阐明直接赋值共享引用、浅拷贝仅复制外层对象而嵌套对象仍共享、深拷贝完全独立创建新对象的机制。结合[[wp:a3]]与[[wp:a6]]模块示例,帮助开发者避免修改副本时意外影响原数据的常见陷阱。
问题起源:
本文的起因在于使用直接赋值b=a处理列表时,实际运行结果与预期不符。随后发现,直接使用等号=对列表进行赋值会引发一系列潜在问题,因此本文旨在记录并区分直接赋值、浅拷贝与深拷贝三者之间的核心差异。
1.列表list赋值方法
在Python中,对象的赋值本质上是简单的对象引用复制,这一点与C++的机制有所不同。
如下例子所示:
a = ['a', 'b', 'c']
b = a # 采用简单的=赋值
print(a==b)
# 下面是输出结果:
True在此种情况下,变量b和a指向完全相同的内存地址,b仅仅是a的一个别名或引用。我们可以通过判断a与b是否相同来验证,返回True,这表明它们的内存地址和内容完全一致。
赋值操作(包括将对象作为参数传递或作为返回值返回)并不会开辟新的内存空间,它仅仅是复制了新对象的引用。换言之,除了变量名b之外,没有产生其他内存开销。
修改a会直接导致b发生变化;同理,修改b也会影响a。下面的示例尝试对b进行修改,在列表b末尾添加新元素'd',通过观察输出结果可以发现:在修改列表b的同时,列表a也随之改变,因为两者共享同一块内存空间。
a = ['a', 'b', 'c']
b = a
b.append('d')
print('a = {}'.format(a))
print('b = {}'.format(b))
# 下面是输出结果:
a = ['a', 'b', 'c', 'd']
b = ['a', 'b', 'c', 'd']2.浅拷贝(shallow copy)
浅拷贝会创建一个新的对象,但其内容仅包含原对象中元素的引用。
浅拷贝主要有三种实现形式:切片操作、工厂函数以及copy模块中的copy函数。
例如对上述列表a进行浅拷贝:
- 1、切片操作:b = a[:] 或者 b = [each for each in a]
- 2、工厂函数:b = list(a)
- 3、copy函数:b = copy.copy(a) #使用时要import copy模块
经过浅拷贝生成的b不再是a,使用is运算符可以发现它们不是同一个对象;通过id()函数查看,发现它们也不指向同一片内存。但是,当我们遍历a和b并打印每个元素的id(x) for x in a 和 id(x) for x in b 时,可以看到二者包含的元素地址是相同的。 在这种情况下,a和b是两个不同的对象,理论上修改b不会影响a。例如执行b.append([4,5])。
代码效果如下:
a = ['a', 'b', 'c', ['yellow', 'red']]
b = a[:] # 采用了切片操作对列表b进行赋值
b.append('green') # 对列表b执行添加元素操作
print('a = {}'.format(a))
print('b = {}'.format(b))
# 下面是输出结果:
a = ['a', 'b', 'c', ['yellow', 'red']] # a中的元素不发生变化
b = ['a', 'b', 'c', ['yellow', 'red'], 'green'] # b中增加了一个元素'green'但是需要注意:浅拷贝之所以称为“浅”,是因为它仅仅只拷贝了一层。如果a中包含嵌套的list,当我们修改该嵌套列表时,情况就会发生变化。 执行 a[3].append(“blue”)。查看b,你将发现b也发生了同样的变化。这是因为,你修改的是嵌套的list。修改外层元素会改变其引用,使它们指向新的位置;但修改嵌套列表中的元素时,列表本身的地址并未发生变化,a和b中的嵌套列表仍然指向同一个内存位置。
代码如下:
a = ['a', 'b', 'c', ['yellow', 'red']]
b = a[:] # 采用了切片操作对列表b进行赋值
a[3].append('blue') # 在a列表中的第3个元素中增加元素'blue',由于a[3]本身也是一个列表,从而是在列表后增加了元素'blue',从输出结果中可以看出来。
print('a = {}'.format(a))
print('b = {}'.format(b))
# 下面是输出结果:
a = ['a', 'b', 'c', ['yellow', 'red', 'blue']]
b = ['a', 'b', 'c', ['yellow', 'red', 'blue']]3.深拷贝
深拷贝只有一种形式,即copy模块中的deepcopy函数。
与浅拷贝对应,深拷贝会拷贝对象的所有元素,包括多层嵌套的元素。因此,它的时间和空间开销相对较高。
同样对列表a,若使用b = copy.deepcopy(a),再修改b将不会影响到a了。即使嵌套的列表具有更深的层次,也不会产生任何影响,因为深拷贝出来的对象根本就是一个全新的对象,不再与原来的对象有任何关联。
实例代码如下:
import copy
a = ['a', 'b', 'c', ['yellow', 'red']]
b = copy.deepcopy(a) # 采用深拷贝对a进行深拷贝操作
b.append('xyz')
print('a = {}'.format(a))
print('b = {}'.format(b))
# 下面是输出结果:
a = ['a', 'b', 'c', ['yellow', 'red']] # 使用深拷贝,对b的修改不会影响到a
b = ['a', 'b', 'c', ['yellow', 'red'], 'xyz']或者用下面的代码:
import copy
a = ['a', 'b', 'c', ['yellow', 'red']]
b = copy.deepcopy(a) # 采用深拷贝对a进行深拷贝操作
a[3].append('crazy')
print('a = {}'.format(a))
print('b = {}'.format(b))
# 下面是输出结果:
a = ['a', 'b', 'c', ['yellow', 'red', 'crazy']]
b = ['a', 'b', 'c', ['yellow', 'red']] # 对a的修改不会影响到b或者用下面的代码:
import copy
a = ['a', 'b', 'c', ['yellow', 'red']]
b = copy.deepcopy(a) # 采用深拷贝对a进行深拷贝操作
a[3].append('crazy')
b.append('dddd')
print('a = {}'.format(a))
print('b = {}'.format(b))
# 下面是输出结果:
a = ['a', 'b', 'c', ['yellow', 'red', 'crazy']]
b = ['a', 'b', 'c', ['yellow', 'red'], 'dddd']4.关于拷贝操作的提醒
- 1、对于非容器类型,如数字、字符以及其它“原子”类型,不存在拷贝一说。产生的都是原对象的引用。
- 2、如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝的效果。
到此这篇关于Python中list列表的赋值方法及遇到问题处理的文章就介绍到这了,更多相关list列表赋值方法内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









