从一段代码开始说事,先上代码:
a = [[1],[2],[3]]
b = [[4,5],[6,7],[7,8]]
for i,j in zip(a,b):
print(i,j)
i = [9]
#i[0] = 8
j[:2]=[1,2]
print(i, j)
print(a)
print(b)
运行的结果:
[1] [4, 5]
[9] [1, 2]
[2] [6, 7]
[9] [1, 2]
[3] [7, 8]
[9] [1, 2]
[[1], [2], [3]]
[[1, 2], [1, 2], [1, 2]]
可以看到修改i的值不会造成数组a的值的修改,而修改j的值会造成数组b的值的修改,这是为什么呢?
可变类型与不可变类型
可变数据类型 :
当该数据类型的对应变量的值发生了改变,那么它对应的内存地址不发生改变,对于这种数据类型,就称可变数据类型。
不可变数据类型:
当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变,对于这种数据类型,就称不可变数据类型。
可变数据类型与不可变数据类型_可变类型和不可变类型-CSDN博客
引用传递与值传递
引用传递(Pass by Reference)
在引用传递中,函数接收的是变量的地址,也就是对象的引用。
当你把一个可变对象(如列表、字典等)传递给函数时,函数内部对该对象的修改会影响到原始的对象。
这是因为函数接收的是对象的引用,而不是对象的副本。
示例:
def modify_list(lst):
lst[0] = 'changed'
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # 输出 ['changed', 2, 3]
值传递(Pass by Value)
在值传递中,函数接收的是变量的值的副本。
当你把一个不可变对象(如整数、字符串等)传递给函数时,函数内部对该变量的修改不会影响到原始的变量。
这是因为函数接收的是变量的值的副本,而不是变量本身。
示例:
def modify_int(x):
x = 'changed'
my_int = 10
modify_int(my_int)
print(my_int) # 输出 10
总的来说,在 Python 中,变量的赋值行为遵循以下规则:
对于可变对象,变量存储的是对象的引用,即引用传递。
对于不可变对象,变量存储的是对象的值,即值传递。
对一开始的问题做出简化,看以下两段代码
import copy
b = [[4,5],[6,7],[7,8]]
c = b[0]
print(id(b[0]))
print(id(c))
d = [1,2]
print(id(d))
print(id(d[0]),id(d[1]))
c = d
print(id(c))
print(id(c[0]),id(c[1]))
print(b)
数出的结果是
140383141636032
140383141636032
140381758507008
140383142445296 140383142445328
140381758507008
140383142445296 140383142445328
[[4, 5], [6, 7], [7, 8]]
可以看到以c=d这种方式改变c并不会修改b的值,因为这是一个最外层引用传递,此时c指定d指向的可变类型(数组)
import copy
b = [[4,5],[6,7],[7,8]]
c = b[0]
print(id(b[0]))
print(id(c))
d = [1,2]
print(id(d))
print(id(d[0]),id(d[1]))
c[:2] = d
print(id(c))
print(id(c[0]),id(c[1]))
print(b)
输出的结果是
140452090626176
140452090626176
140450707604352
140452091609328 140452091609360
140452090626176
140452091609328 140452091609360
[[1, 2], [6, 7], [7, 8]]
可以看到以c[:2]=d的这种方式,就是先进入c指向的可变数据类型的地址也就是进入到b[0]内部,找到b[0]内部存储的两个子对象的地址,再更改这两个子对象的地址,所以以这种方法更改c也会跟着更改原始的b。
看这个图会有更深入的理解
还看不懂看这个
一文读懂 Python 值传递和引用传递_python 引用传递-CSDN博客
浅拷贝与深拷贝
浅拷贝
只拷贝了对象,对象内的元素并不会发生拷贝。
深拷贝
不仅拷贝对象,对象内的元素也会发生拷贝。
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
上代码
import copy
b = [[4,5],[6,7],[7,8]]
print(id(b))
print(id(b[0]),id(b[1]))
c = copy.copy(b)
print(id(c))
print(id(c[0]),id(c[1]))
e = copy.deepcopy(b)
print(id(e))
print(id(e[0]),id(e[1]))
运行结果
140133587657344
140134970720384 140133587652608
140133587657216
140134970720384 140133587652608
140133587657152
140133587847808 140133587847232
与上述描述相同
但是浅拷贝和深拷贝对处理可变类型数据与不可变类型数据还会有一些差异
更详细的内容请阅读:
一篇就懂:python浅拷贝copy与深拷贝deepcopy_python deepcopy-CSDN博客
可以结合以下代码来理解一下
import copy
b = [[4,5],[6,7],[7,8],9]
e = [1,9]
print(id(b))
print(id(b[0]),id(b[1]))
c = copy.copy(b)
print(c)
b[0][:] = e
print(b)
print(c)
b[3] = 3
print(b)
print(c)
上述代码的输出是
140440909761280
140442292905920 140440909723776
[[4, 5], [6, 7], [7, 8], 9]
[[1, 9], [6, 7], [7, 8], 9]
[[1, 9], [6, 7], [7, 8], 9]
[[1, 9], [6, 7], [7, 8], 3]
[[1, 9], [6, 7], [7, 8], 9]
参考文献
浅拷贝与深拷贝的区别_深拷贝和浅拷贝的区别-CSDN博客
可变数据类型与不可变数据类型_可变类型和不可变类型-CSDN博客
一文读懂 Python 值传递和引用传递_python 引用传递-CSDN博客
一篇就懂:python浅拷贝copy与深拷贝deepcopy_python deepcopy-CSDN博客