Python中List传值:浅拷贝与深拷贝的区别及内存地址分析
本文详细解析Python中List传值的两种常见方式。通过对比直接赋值、浅拷贝(copy)与深拷贝(deepcopy)的行为,揭示多层嵌套列表在修改时出现的地址共享问题。文章结合具体代码示例与内存地址输出,说明何时使用copy方法足以应对,何时必须引入copy.deepcopy以确保数据独立性,帮助开发者避免因引用共享导致的意外数据变更。
在Python开发过程中,因列表传值机制导致的Bug曾让我耗费大量时间排查,因此特此记录这一常见陷阱及其解决方案。
单层列表的传值行为分析
对于单层列表,若直接使用等号(=)进行赋值,两个变量将指向同一内存地址。此时,修改任意一个列表的元素,另一个列表也会随之改变。这可以通过观察id()函数返回的地址值来验证:两者完全一致。若希望获得一个独立副本,即在接收值后,原列表的修改不影响新列表,必须使用copy方法。该方法会在内存中开辟新的地址空间存放新列表,从而实现数据隔离。
a = [ 1,2,3]
b = a
a[0] = a[0] +1
print(a)
print(b)
print(id(a[0]))
print(id(b[0]))
print("*"*20)
a1 = [5,6,7]
b1 = a1.copy()
a1[0] = a1[0] + 1
print(a1)
print(b1)
print(id(a1[0]))
print(id(b1[0]))
运行结果如下:
[2, 2, 3]
[2, 2, 3]
140729246982592
140729246982592
********************
[6, 6, 7]
[5, 6, 7]
140729246982720
140729246982688
多层嵌套列表的传值陷阱
这是实践中容易忽视的细节。虽然对第一层列表使用了copy方法,但对于嵌套的内层列表,copy仅复制了引用而未复制对象本身。因此,修改内层元素时,两个列表仍会相互影响,因为内层元素的内存地址依然相同。
a = [[1,2,4],[3,5,8],[],[]]
b = a.copy()
print("打印数组地址:")
print(id(a))
print(id(b))
print("*"*40)
print("打印内层元素地址:")
print(id(a[0]))
print(id(b[0]))
print(id(a[0][0]))
print(id(b[0][0]))
print("*"*40)
a.append(4)
a[1][0] = a[1][0] + 1
print(a)
print(b)
print(id(a[4]))
相关输出显示:
打印数组地址:
1938878979528
1938879043976
****************************************
打印内层元素地址:
1938877731208
1938877731208
140729246982560
140729246982560
****************************************
[[1, 2, 4], [4, 5, 8], [], [], 4]
[[1, 2, 4], [4, 5, 8], [], []]
140729246982656

正确的赋值方式应使用copy模块中的deepcopy函数。
import copy
a = [[1,2,4],[3,5,8],[],[]]
# b = a.copy()
b = copy.deepcopy(a)
即通过copy.deepcopy进行赋值。此时,无论修改a还是b的内层元素,另一个列表均不会受到影响。值得注意的是,Python的设计在内存优化上非常精妙:如果不对a和b内部的元素值进行重新赋值(即不改变其指向),那么a和b内部元素的内存地址在初始状态下仍然是相同的。只有当某一方尝试修改自身地址内的值时,Python才会为该元素分配新的内存地址,从而实现真正的独立。
import copy
a = [[1,2,4],[3,5,8],[],[]]
# b = a.copy()
b = copy.deepcopy(a)
print("打印数组地址:")
print(id(a))
print(id(b))
print("*"*40)
print("打印内层元素地址:")
print(id(a[0]))
print(id(b[0]))
print("0"*50)
print(id(a[0][0]))
print(id(b[0][0]))
print("10"*50)
print("\n")
print(a[1][0])
print(b[1][0])
print(id(a[1][0]))
print(id(b[1][0]))
print("10"*50)
print("\n")
# print("*"*40)
# a.append(4)
a[1][0] = a[1][0] + 1
#
# print(a)
# print(b)
# print(id(a[4]))
# print("\n")
print("10"*50)
print(a[1][0])
print(b[1][0])
print(id(a[1][0]))
print(id(b[1][0]))
print("10"*50)
print("\n")
b[1][0] = b[1][0] + 2
print(id(b[1][0]))
deepcopy的最终输出结果如下:
打印数组地址:
1696261290248
1696261352200
****************************************
打印内层元素地址:
1696257692040
1694368707272
00000000000000000000000000000000000000000000000000
140729133343136
140729133343136
1010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
3
3
140729133343200
140729133343200
1010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
1010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
4
3
140729133343232
140729133343200
1010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
140729133343264
以上即为Python中List传值的完整解析,涵盖了从浅层到深层的拷贝机制及内存表现。更多关于Python列表操作的技巧,请参阅本站其他相关文章。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









