python赋值浅拷贝深拷贝的不同

python中所有的事物都是对象

一:赋值

首先看下面这个例子,id查看对象的地址

x = 666
id(x)  # 2650482715088
y = x
id(y)  # 2650482715088

因为y=x只是把y指向了x指向的对象,那你可能会好奇,修改x,那么y会一起变化吗?其实是不会的,因为当你修改x的时候其实是创建了一个新的对象,并把x指向这个对象

x = 666
id(x)  # 2650482966608
y = x
id(y)  # 2650482966608
x=x+1
id(x)  # 2650482967344
id(y)  # 2650482966608
print(y)  # 666

但是这只针对不可变对象(int,string等)可变对象(list等)是会发生改变的。因为不可变对象发生改变时其实是创建了新的对象并赋值

x=[1,2,3]
y=x
id(x)  # 2650483005960
id(y)  # 2650483005960
x.append(5)
id(x)  # 2650483005960
id(y)  # 2650483005960
print(x)  # [1, 2, 3, 5]
print(y)  # [1, 2, 3, 5]

但是如果是显示的改变可变对象,不会影响y的值

x = [1,2,3]
id(x)  # 2650482808648
y = x
id(y)  # 2650482808648
x = x + [4]
id(x)  # 2650482864712
print(x)
print(y)
"""
[1, 2, 3, 4]
[1, 2, 3]
"""

因为实际上是新建了一个对象[1,2,3,4]并让x指向这个新的对象

2 缓存池

再看下面的例子,x和y指向相同的对象

x = 666
y = x
id(x)  #  2040304935632
id(y)  #  2040304935632

而下面则不同

a = 888
id(a)  # 2650482715440
b = 888
id(b)  # 2650482715568

因为是创建了两个对象,分别赋值给a,b

  • 第一次 : 仅仅在内存中创建一个整型对象666, xy都是这个整型对象的标签
  • 第二次 : 在内存中创建两个整型对象888,分别用ab作为标签

注意:python有小整数池(-5 ~ 256)

x=2
y=2
id(x)  # 140736101317728
id(y)  # 140736101317728

对于xy这样小的且常用的整型值,python在内存中并不是创建两个对象,而是一个对象.此时xy都是整型对象2的标签。python的小整数池是为了提高缓存效率,降低’常用’对象频繁创建撤销频率

类似的,字符串也有缓存池,缓存机制符合一下规则

字符串都会被缓存复用,但是只能是字母和数字和下划线构成的

s1="sssjkjksTjasdfgssshjkl哈"
len(s1)
s2="sssjkjksTjasdfgssshjkl哈"
id(s1)  # 2040304847280
id(s2)  # 2040304847664
s1 is s2  #  False
s1="sssjkjks1Tjasdfgssshjklssssssss_"
len(s1)
s2="sssjkjks1Tjasdfgssshjklssssssss_"
id(s1)  # 2040305172928
id(s2)  # 2040305172928
s1 is s2

接下来就是浅拷贝和深拷贝

浅拷贝和深拷贝

首先回顾一下刚才讲的赋值

abc=[1,'string',['python','java',25]]
zxc=abc
id(abc)  # 2040305182344
id(zxc)  # 2040305182344

到目前为止可能没什么疑问,修改里面的值,abc,zxc都会一起变化

abc[0]=2
abc[2].append(5)
print(abc)
print(zxc)
"""
[2, 'string', ['python', 'java', 25, 5]]
[2, 'string', ['python', 'java', 25, 5]]
"""

但是注意

abc=[1,'string',['python','java',25]]
zxc=abc
id(abc)  
id(zxc)  
print([id(x) for x in abc])
print([id(x) for x in zxc])
"""
[140736101317696, 2042134608752, 2040305268936]
[140736101317696, 2042134608752, 2040305268936]
"""
abc[0]=2
abc[2].append(5)
print(abc)
print(zxc)
"""
[2, 'string', ['python', 'java', 25, 5]]
[2, 'string', ['python', 'java', 25, 5]]
"""
print([id(x) for x in abc])
print([id(x) for x in zxc])
"""
[140736101317728, 2042134608752, 2040305268936]
[140736101317728, 2042134608752, 2040305268936]
"""

注意:这里abc[0]的地址是变化了,但是abc[2]的地址不变(因为int是不可变对象 修改意味着创建新的对象并赋值,而list是可变对象)

请添加图片描述

接下来是浅拷贝

abc=[1,'string',['python','java',25]]
zxc=copy.copy(abc)
id(abc)  # 2040305143752
id(zxc)  # 2040321956104
print([id(x) for x in abc])
print([id(x) for x in zxc])
"""
[140736101317696, 2042134608752, 2040305323016]
[140736101317696, 2042134608752, 2040305323016]
"""

注意到abc和zxc的地址已经不同了,但是它们里面元素的地址还是指向相同的地址

abc[0]=2
abc[2].append(5)

print(abc)
print(zxc)
"""
[2, 'string', ['python', 'java', 25, 5]]
[1, 'string', ['python', 'java', 25, 5]]
"""
print([id(x) for x in abc])
print([id(x) for x in zxc])
"""
[140736101320832, 2042134608752, 2040305323016]
[140736101317696, 2042134608752, 2040305323016]
"""

abc[0]的地址和zxc[0]的地址不同了,值也不同了
请添加图片描述

深拷贝

abc=[1,'string',['python','java',25]]
zxc=copy.deepcopy(abc)
id(abc)  # 2326337710920
id(zxc)  # 2326338046664
print([id(x) for x in abc])
print([id(x) for x in zxc])
"""
[140736246742080, 2326022994736, 2326340682056]
[140736246742080, 2326022994736, 2326340706824]
"""
abc[0]=99
abc[2].append(5)
print(abc)
print(zxc)
"""
[99, 'string', ['python', 'java', 25, 5]]
[1, 'string', ['python', 'java', 25]]
"""
print([id(x) for x in abc])
print([id(x) for x in zxc])
"""
[140736246745216, 2326022994736, 2326340682056]
[140736246742080, 2326022994736, 2326340706824]
"""

在这里插入图片描述

注意:对于非容器类型(如数字、字符串、和其他’原子’类型的对象)没有拷贝这一说

总结:

"abc=zxc"赋值时,没有开辟新的内存空间,所有变量的修改都会互相影响

浅拷贝:对abc非可变对象的修改不会影响到zxc,但对可变对象的修改会影响到zxc

深拷贝:所有变量的修改都不会互相影响

浅拷贝的情况:

1 切片zxc=abc[:]

2 copy.copy

深拷贝的情况:

1 copy.deepcopy

2 DataFrame.copy()

参考资料:
1图解python中赋值、浅拷贝、深拷贝的区别
2 Python: 函数参数是值传递还是引用传递?
3 可变对象和不可变对象

猜你喜欢

转载自blog.csdn.net/m0_52118763/article/details/124771251
今日推荐