Python中的赋值、copy()、deepcopy()

(本篇文章事我16年写的,从我的网站上copy过来了,嗯,深拷贝)

一直想对这部分做个整理,写出个教程,但却没有动手写,今天就着这部分写个完整的教程。

说正式内容之前,要说一下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、如果元祖变量只包含原子类型对象,则不能深拷贝。

--------EOF--------- 
本文微信分享/扫码阅读

猜你喜欢

转载自blog.csdn.net/hbnn111/article/details/80859115