Python 浅拷贝与深拷贝

为了以后不再工程中犯不必要的错误,还是要对python的拷贝等概念要加已熟悉的,虽然都是调用python的copy基本库,但还是有本职的区别

首先还是要从python的基本赋值说起

赋值

a = '1'
b = a
print a is b
print id(a)
print id(b)

结果

True
4499690480
4499690480

很明显,b赋值于a,看似是八竿子打不着的两个变量,虽然值相同,但追根溯源后本质是一个东西,id均指向同一个

所以

在python中,对象赋值实际上都是进行对象引用(内存地址)传递
当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用

浅拷贝

使用copy库进行验证

使用copy()

woodz = ['student', 16, ['wow', 'war3', 'cs']]
billy = copy.copy(woodz)
print [id(i) for i in woodz, billy]  
billy[2].append('communicate')
print billy
print woodz  
print billy is woodz
print billy == woodz

结果

[4500610080, 4500610296]
['student', 16, ['wow', 'war3', 'cs', 'communicate']]
['student', 16, ['wow', 'war3', 'cs', 'communicate']]
False
True

使用copy之后,两个列表的id都不同了,但是值还是相同

浅拷贝是相当于创建一个全新的对象,但修改了新对象后依然能影响原对象,本质上还是对原对象的引用

其他的浅拷贝形式:切片和list()

切片拷贝
woodz = ['student', 16, ['wow', 'war3', 'cs']]
billy = woodz[:]
print [id(i) for i in woodz, billy]  
billy[2].append('communicate')
print billy is woodz
[4551782056, 4551818272]
False
list()
woodz = ['student', 16, ['wow', 'war3', 'cs']]
billy = list(woodz)
print id(woodz) == id(billy)
billy[2].append('Python')
print billy
print woodz 
False
['student', 16, ['wow', 'war3', 'cs', 'Python']]
['student', 16, ['wow', 'war3', 'cs', 'Python']]

WTF?

以上两种方式的拷贝和copy()的拷贝无异,切片拷贝和list(类似的工厂函数)拷贝都可以实现浅拷贝

归纳一下

浅拷贝的方式

  1. copy.copy()
  2. 切片
  3. list()等工厂函数

浅拷贝的特点

浅拷贝是相当于创建一个全新的对象,但修改了新对象后依然能影响原对象虽然本质上还是对原对象的引用

深拷贝

使用copy库的deepcopy()函数

woodz = ['student', 16, ['wow', 'war3', 'cs']]
billy = copy.deepcopy(woodz)
print id(woodz) == id(billy)  # False
billy[2].append('Python')
print billy
print woodz  # 没有影响到原来的对象
False
['student', 16, ['wow', 'war3', 'cs', 'Python']]
['student', 16, ['wow', 'war3', 'cs']]
True
4550902768
4550902768

若浅拷贝是现任对象与前任有藕断丝连的嫌疑,那么深拷贝就是老四不相往来,根本不会影响原来的对象,拷贝对象与被拷贝对象没任何关系,id不同,后者修改也不会波及前者,经deepcopy()过的对象都是新的.

深拷贝跟浅拷贝类似,深拷贝也会创建一个新的对象,并重新开辟一块新的内存已提供引用,再如何修改新对象也不会影响到原对象

总结

基本上python中的浅拷贝和深拷贝的区别主要就是这几点

  1. 浅拷贝使用copy.copy() , 切片,工厂函数(list()等)作用拷贝对象. 在修改拷贝对象后,也会影响原对象
  2. 深拷贝使用copy.deepcopy()这一种方式作用拷贝对象.拷贝后的对象无论如何修改,也不会影响原对象

另外

q:元组,数字,字符串能否也能拷贝?

A:无意义

还是那句python的老话

对象赋值实际上是简单的对象引用,在标准类型中,可改变的类型只有列表和字典,所以拷贝能服务于它们.

不可改变的数字,字符串,元组这些类型用不上,除了"元组中的元素有可变类型"的特殊情况之外

end
抛砖引玉

本文章首发在 PythonCaff