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 可变对象和不可变对象