个人认为可变对象和深浅拷贝是未来编程首要注意的事情,毕竟如果是这个上面出现bug应该比较难察觉。

首先贴概念:

在python中,拷贝就是为对象添加新的名字。

浅拷贝:只复制引用,而不复制对象本身的情况。

深拷贝:复制内容,而非简单复制引用(可使用copy模块中的deepcopy函数)

我们只需要记住一点,python中的list列表是可变对象,当列表被封装(如list1 = list1 + [list2]或者list1.append(list2))之后,作为其他列表的一个部分时,其会随着相同引用对象的改变而改变;相反如果列表被拆分了(如list1 = list1 + list2或者list1.extend(list2)),我们就不用担心里头的元素会随着其他对象的改变而发生变化。

下面给出运行实例:

list1 = [1,2,3]
list2 = [5,6,7]
list1.append(list2) # 列表进行了封装,仅改变list1本身
list2[-1] = 100
print list1 ## 对应结果
print list2 ##
list1 = [1,2,3]
list2 = [5,6,7]
list1 = list1 + [list2] # 列表进行了封装,并生成新列表对象
list2[-1] = 100
print list1 ##
print list2 ##
 
list1 = [1,2,3]
list2 = [5,6,7]
list1 = list1 + list2 # 生成新列表对象
list2[-1] = 100
print list1 ##
print list2 ##
list1 = [1,2,3]
list2 = [5,6,7]
list1.extend(list2) # 仅改变list1本身
list2[-1] = 100
print list1 ##
print list2 ##

结果

# [1, 2, 3, [5, 6, 100]]
# [5, 6, 100]
# [1, 2, 3, [5, 6, 100]]
# [5, 6, 100]
# [1, 2, 3, 5, 6, 7]
# [5, 6, 100]
# [1, 2, 3, 5, 6, 7]
# [5, 6, 100]

这里还要介绍一下python里头的两个相等的判断函数:

is: 判断两个name是否引用的同一对象,使用方法 list2 is list3,返回true/false

== : 判断两个name对引用的value是否一致,使用方法 list2 == list3

list1 = [1,2,3]
list2 = list1[:] #相当于深复制,生成一个新对象
list3 = list1 # 浅复制,引用相同对象,可节省储存空间
print list1 is list2
print list1 == list2
print list1 is list3
print list1 == list3

结果为

# False
# True
# True
# True

最后给出一个防不胜防的例子,这个例子里头一个封装好的列表并没有被其他name引用,而且copyLst采用了复制操作,但是由于封装的原因,所有Lst中的封装列表元素都发生了变化,大家自己感受下= =

strLst = ["hi", "mom", "dad", ["grandma", "grandpa"]]
newLst = strLst
copyLst = strLst[:]
 
strLst[0] = "bye"
newLst[1] = "mother"
copyLst[2] = "father"
copyLst[-1][0] = "nanna"
 
print strLst
print newLst
print copyLst

运行结果

# ['bye', 'mother', 'dad', ['nanna', 'grandpa']]
# ['bye', 'mother', 'dad', ['nanna', 'grandpa']]
# ['hi', 'mom', 'father', ['nanna', 'grandpa']]

以后大家编程时遇到列表里头嵌套有封装列表的,创建其他name之后需要小心再小心啦~解决方法就是对于重要的全局变量采用不可变对象比如元组tuple,而对不可变对象进行操作,每次都会创建一个新的对象哦~相反,对于可变对象来说,改变该对象或者创建一个新对象都是有可能发生的(比如list.append(9)是改变原对象而list = list + [9]则是创建了一个新对象)。