Python中的Copy和Deepcopy
一,Python的对象:
Python存在大量的对象,我们一般提到的对象都是C中的结构体在堆中申请的一块内存(以CPython为例),每一个对象都有ID,可以通过ID(Object)获得。对象的范围包括内建类型的对象(如int类型的对象123,str的对象"test",list的对象[],生成器对象等等)、用户自建类型的对象、函数(lambda函数,生成器函数)、类型、Ture、False(bool类型的对象)、None(NoneType的对象)。
1 print id(123) 2 print id("test") 3 print id([]) 4 print id(xrange(1, 3)) 5 print id(lambda v: v) 6 print id(int) 7 print id(True) 8 print id(None) 9 10 print type(123) 11 print type("test") 12 print type([]) 13 print type(xrange(1, 3)) 14 print type(lambda v: v) 15 print type(int) 16 print type(True) 17 print type(None)
1 19257680 2 19062656 3 27888896 4 19590176 5 27863344 6 505361888 7 505379788 8 505354444 9 <type 'int'> 10 <type 'str'> 11 <type 'list'> 12 <type 'xrange'> 13 <type 'function'> 14 <type 'type'> 15 <type 'bool'> 16 <type 'NoneType'>
猜想int类型、True和None都是在高地址中,而动态生成的都是位于低地址中。
二,Python的引用计数:
所有对象都采用引用计数,给对象分配新名词或者将其放入容器中,这个对象的引用计数都会增加。当离开作用域或者被重新赋值的时候,这个对象的引用计数就会减少。当减少为0,垃圾收集器会将其销毁。
Python中的对象分为immutable和mutabel,作为immutable的int、char和tuple,相同的值在内存中只会生成一次,每次将其赋值到变量中,便会增加一个引用计数。而作为mutable的list、dict、xrange(生成器)和lambda(函数),每次生成都会在内存中生成一个新的对象。
另外:传参的时候,若参数为mutable,则传的是引用,若参数为immutable,则传的是值。
1 a = 123 #创建一个值为123的对象 2 b = 123 #增加123的引用计数 3 a = 111 #创建一个值为111的对象,减少123的引用计数 4 b = a #增加111的引用计数 5 6 c = [] #创建一个空队列 7 d = [] #创建一个新的空队列 8 c = [] #再创建一个新的空队列,减少原队列的引用计数 9 c = d #增加d队列的引用计数,减少c队列的引用计数 10 c.append(1) #创建一个值为1的对象,不改变原队列的引用计数
三,Shallow copy和Deep copy的区别
Python document中是这么说的:
- A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
- A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
这其中的区别就是浅复制会简单的复制引用,不管他是mutable还是immutable。而深复制则会递归地复制immutable对象引用,对于mutable,则会新建一个对象。
1 import copy 2 a = [1, 2] 3 b = [a, 3] 4 c = copy.copy(b) #c = [[1, 2], 3] 5 d = copy.deepcopy(b) #d = [[1, 2], 3] 6 a.append(3) #c = [[1, 2, 3], 3] 7 #d = [[1, 2], 3]
四,如何控制Shallow copy和Deep copy?
在deepcopy时候,会遇到问题,比如有对象中有一个指向用户信息的列表,用户希望在复制的时候直接引用原列表。这个时候可以使用定制功能。
可以在类型中定义__copy__和__deepcopy__实现shallow copy和deep copy,当用户下次调用copy.copy和copy.deepcopy时,便会调用__copy__和__deepcopy__。
1 import copy 2 class Example(dict): 3 def __init__(self, needcopy={}, userinfo={}): 4 super(Example, self).__init__() 5 self.need = needcopy 6 self.user = userinfo 7 8 def __deepcopy__(self, memo): 9 if memo is None: 10 memo = {} 11 result = self.__class__() 12 memo[id(self)] = result 13 for key in self.need.keys(): 14 result.need = copy.deepcopy(self.need) 15 result.user = self.user 16 return result 17 18 needcopy = {"price": 100} 19 userinfo = {"user": "vincent"} 20 a = Example(needcopy, userinfo) 21 b = copy.deepcopy(a) 22 needcopy["title"] = "no" 23 userinfo["passwd"] = "none" 24 print "need: ", a.need, "; user: ", a.user 25 print "need: ", b.need, "; user: ", b.user
1 need: {'price': 100, 'title': 'no'} ; user: {'passwd': 'none', 'user': 'vincent'} 2 need: {'price': 100} ; user: {'passwd': 'none', 'user': 'vincent'}