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, x 和 y都是这个整型对象的标签
- 第二次 : 在内存中创建两个整型对象888,分别用a和b作为标签
注意:python有小整数池(-5 ~ 256)
x=2 y=2 id(x) # 140736101317728 id(y) # 140736101317728
对于x和y这样小的且常用的整型值,python在内存中并不是创建两个对象,而是一个对象.此时x和y都是整型对象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 可变对象和不可变对象



