Python
初学者系列
关于列表中包含列表在复制时会自然而然地思考
listA = [1, [2, 3, 4], 5]
listB = listA[:]
listB[0] = 111
print(listA)
print(listB)
#结果
[1, [2, 3, 4], 5]
[111, [2, 3, 4], 5]
上面这些代码很容易出现在各种python初学者的书中,我所使用的《Python编程从入门到实践》的三件套也一样。 他似乎让人简单地相信了:“啊!我创建了一个全新的列表,这是一个深拷贝(deepcopy),耶!”
并且在书中P55的4.4.3中明确说明了使用上述方法会创建一个原有列表的副本。可为什么对上述代码继续执行下述操作时,会产生一个意想不到的错误。
listB[1][0] = 8
print(listA)
#结果
[1 ,[8, 3, 4], 5]
这是为什么呢,不是说好了会新copy出一个副本吗?
是谁欺骗了你!
所以需要了解的是:python中所有的变量更类似于是对于一个“值”的标签,并且这是可以简单验证,就可以很好地说服别人。你只需要输入以下代码:
print(listA[0] is listB[0])#listA的第一个元素(数值)和listB中第一个元素(数值)是同一个吗?
print(listA[1] is listB[1])
#结果
True
True
这说明了什么呢?
表示就算真的listB是一个listA的新的副本,但其中不论是值还是列表(即列表中的列表)都是同一个,如果你还是不相信那你可以详细查询每一个参数的地址。
print(id(listA))
print(id(listB))
print(id(listA[0]))
print(id(listB[0]))
print(id(listA[1]))
print(id(listB[1]))
#结果
3259111553032 #listA的地址
3259111808328 #listB的地址
140709651476224 #listA中第一个元素(数值)的地址
140709651476224 #listB中第一个元素(数值)的地址
3259141103880 #listA中第二个元素(列表)的地址
3259141103880 #listB中第二个元素(列表)的地址
这应该足以说服你了吧!
上述结果中的数字你们肯定不会和我一样,但其中彼此相同的id就足以说明其中的逻辑
说明他们其实都是相当于一个标签。也就是说当我们在第一步中执行“listB[0] = 111”时,只是改动了listB第一个位置的标签,让它指向了111。不论是listA还是listB它们的第二个元素的标签都是指向[2,3,4]这个列表。
而当我们进行“listB[1][0] = 8”时,我们相当于通过listB的第二个标签索引到了这个列表 [2,3,4] ,并且在中索引到了 [2,3,4] 的第一个标签,并修改了这个标签对应的值,将 2 >>8。
listA listB
[ 0 ] ------> 1 <------ [ 0 ]
[ 1 ] ------> [I don't care] <------ [ 1 ]
[ 2 ] ------> 1 <------ [ 2 ]
"""执行
listB[0] = 111
"""
listA listB
[ 0 ] ------> 1 [ 0 ] ------> 111
[ 1 ] ------> [I don't care] <------ [ 1 ]
[ 2 ] ------> 1 <------ [ 2 ]
"""执行
listB[1][0] = 8
"""
listA listB
[ 0 ] ------> 1 [ 0 ] ------> 111
[ 1 ] ------> [I don't care] <------ [ 1 ]
[ 2 ] ------> 1 <------ [ 2 ]
"""
请问修改了listA还有listB中的标签吗?
你只是对[I dont't care]进行了修改,我都说了我I don't care了,你还指望你修改了能改变什么吗?
死舔狗!
"""
你想要改变listB[1],想和listA[1]彻底毫无关联,并且不改变它的值仅仅需要重新赋值即可
listB[1] = [2, 3, 4]
print(id(listA[1]))
print(id(listB[1]))
listB[1][0] = 8
print(listA)
#结果
3259110893320
3259141078280
[1, [2, 3, 4], 5]
拿下!!!