Python中的赋值、copy()、deepcopy()
一直想对这部分做个整理,写出个教程,但却没有动手写,今天就着这部分写个完整的教程。
说正式内容之前,要说一下Python的内存管理机制。
在Python中,变量在第一次赋值时自动声明,在创建---也就是赋值的时候,解释器会根据语法和右侧的操作数来决定新对象的类型。
一个对象必须有一个PyObject,它就是一个引用计数器,用来跟踪对象的。当一个对象有了一个新的引用时,它的计数器(ob_refcnt)就会加1,当引用它的变量被删除,计数器就减1,当引用计数为0的时候,该对象就会被回收。 Python就是使用引用计数来跟踪和回收垃圾。
x = 3
语句 x=3,创建一个整数型对象并将其引用赋值给了x,x是第一个引用,该对象的引用计数为1
当一个对象(的引用)又被赋值到其他变量,或做参数传递等,该对象的一个新的引用(或叫别名)被创建,则该对象的引用计数自动+1。
以下都会增加引用计数:
y = x #赋值引用
foo(x) #做参数传递
array = [1,2,x,'a'] #成为容器对象的一个元素
以下都会减少引用计数:
del x #del显式销毁
x = True #对象的一个别名被赋值给其他对象
array.remove(x) #对象被从窗口对象中移除
说了内存管理,现在说说赋值、copy、deepcopy的区别。
1、赋值
y=x
这条语句就是一个赋值语句,赋值语句只是传递对象的一个引用,也就是说变量y和x都指向同一个对象,都是对象的一个引用。
a=[1,2,3]
>>> s=a
>>> a.append(4)
>>> a
[1, 2, 3, 4]
>>> s
[1, 2, 3, 4]
>>> id(a)
44481224L
>>> id(s)
44481224L
看上面的例子,变量s和a的内存地址都是一样,也就是说两者指向同一个对象。 不过这里要解释另外一个现象,以免误会。先看例子:
>>> x
'asd'
>>> y=x
>>> x='ff'
>>> x
'ff'
>>> y
'asd'
为什么这里x变了,y却没有跟着变呢,不是指向同一个对象吗?这里就涉及到另一个问题了。就是Python中字符串,数值,元祖是不可变的。当你重新为变量x赋值为'ff'时,实际上是又创建了一个新的对象,与原来的对象已经毫无关系了。
2、copy()
copy()是浅拷贝的一种,它拷贝了一个对象的副本,即复制了对象本身,但是对象内部的元素仍然使用引用。
说copy()是浅拷贝的一种,那肯定还有其他的浅拷贝的方式,Python中还有:
1、列表切片;
2、使用工厂函数(list/dict/set)。
看例子:
>>> s=[1,2,[1,2]]
>>> a=s[:]
>>> a[2].append(2)
>>> a
[1, 2, [1, 2, 2]]
>>> s
[1, 2, [1, 2, 2]]
看上面的例子, a和s的id不一致,即是不同的对象。但是对于列表中的元素, 他们存储的是对象的引用,即引用还是相同的。a[0] is s[0] 返回True。不过您这要注意,此时如果你要改变a[0],即不可变元素,此时b是不会跟着变的,为什么?因为当你改变a[0]的值的时候,a[0]就会存储新的对象的地址,而s仍然保留原对象的地址。
3、deepcopy()
如果我们想要不仅对象本身实现拷贝,也要拷贝对象中的元素,即完全独立的一个对象,那么就用deepcopy()。
>>> s
[1, 2, [1, 2, 2]]
>>> q
[1, 2, [1, 2, 2]]
>>> s[2].append(3)
>>> s
[1, 2, [1, 2, 2, 3]]
>>> q
[1, 2, [1, 2, 2]]
注意:
1、对于非容器类型(如数字、字符串、和其他‘原子’类型的对象)没有被拷贝一说。
2、如果元祖变量只包含原子类型对象,则不能深拷贝。
微信分享/微信扫码阅读