python中如何做到复制列表aa中数据到新列表bb,修改列表中元素,保存到新
列表bb,⽽。。。
最近遇到⼀个问题,最终解决,记录下来。
现在有⼀个列表aa,要对其中的列表中数据修改,然后保存到新列表bb,⽽希望原列表aa保持不变(元素都不变)。⾸先按照⾃⼰思路写代码,如下:
aa = [['1_1', 2, 3], ['2_4', 5, 6], ['3_7', 8, 9]]
bb = []
temp = 0
print("before aa=", aa) # 打印出原列表aa
for i in range(len(aa)):
bb.append(aa[i])
temp = bb[i][0].split('_')[-1]
bb[i][0] = temp
print("after aa=", aa) # 打印出原列表aa,查看它时候发⽣变化
print("bb=", bb)
输出结果如下,发现列表aa发⽣变化,达不到⽬的!
本来以为把列表aa的每⼀⾏添加到新列表bb,然后在bb上做出修改,总以为这样列表aa不会发⽣变化,结果事与愿违!看来还需另寻他法,后来查到可以使⽤deepcopy函数从列表aa复制数据到bb,如下:
import copy
aa = [['1_1', 2, 3], ['2_4', 5, 6], ['3_7', 8, 9]]
bb = copy.deepcopy(aa)
temp = 0
print("before aa=", aa)
for i in range(len(aa)):
temp = bb[i][0].split('_')[-1]
bb[i] = temp
print("after aa=", aa)
print("bb=", bb)
⾸先直接上结论: —–我们寻常意义的复制就是深复制,即将被复制对象完全再复制⼀遍作为独⽴的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产⽣影响。
—–⽽浅复制并不会产⽣⼀个独⽴的对象单独存在,他只是将原有的数据块打上⼀个新标签,所以当其中⼀个标签被改变的时候,数据块就会发⽣变化,另⼀个标签也会随之改变。这就和我们寻常意义上
的复制有所不同了。
对于简单的 object,⽤ shallow copy 和 deep copy 没区别
复杂的 object, 如 list 中套着 list 的情况,shallow copy 中的 ⼦list,并未从原 object 真的「独⽴」出来。也就是说,如果你改变原object 的⼦ list 中的⼀个元素,你的 copy 就会跟着⼀起变。这跟我们直觉上对「复制」的理解不同。
看不懂⽂字没关系我们来看代码:
>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin ⾥边有三个元素:1, 2,[3, 4]
>>> cop1 = py(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False
#cop1 和 cop2 看上去相同,但已不再是同⼀个object
>>> origin[2][0] = "hey!"
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
python新手代码错了应该怎么改
#把origin内的⼦list [3, 4] 改掉了⼀个元素,观察 cop1 和 cop2
可以看到 cop1,也就是 shallow copy 跟着 origin 改变了。⽽ cop2 ,也就是 deep copy 并没有变。
似乎 deep copy 更加符合我们对「复制」的直觉定义: ⼀旦复制出来了,就应该是独⽴的了。如果我们想要的是⼀个字⾯意义的「copy」,那就直接⽤ deep_copy 即可。
那么为什么会有 shallow copy 这样的「假」 copy 存在呢? 这就是有意思的地⽅了。
python的数据存储⽅式
Python 存储变量的⽅法跟其他 OOP 语⾔不同。它与其说是把值赋给变量,不如说是给变量建⽴了⼀个到具体值的 reference。
当在 Python 中 a = something 应该理解为给 something 贴上了⼀个标签 a。当再赋值给 a 的时候,就好象把 a 这个标签从原来的something 上拿下来,贴到其他对象上,建⽴新的 reference。 这就解释了⼀些 Python 中可能遇到的诡异情况:
>> a = [1, 2, 3]
>>> b = a
>>> a = [4, 5, 6] //赋新的值给 a
>>> a
[4, 5, 6]
>>> b
[1, 2, 3]
# a 的值改变后,b 并没有随着 a 变
>>> a = [1, 2, 3]
>>> b = a
>>> a[0], a[1], a[2] = 4, 5, 6 //改变原来 list 中的元素
>>> a
[4, 5, 6]
>>> b
[4, 5, 6]
# a 的值改变后,b 随着 a 变了
上⾯两段代码中,a 的值都发⽣了变化。区别在于,第⼀段代码中是直接赋给了 a 新的值(从 [1, 2, 3] 变为 [4, 5, 6]);⽽第⼆段则是把list 中每个元素分别改变。
⽽对 b 的影响则是不同的,⼀个没有让 b 的值发⽣改变,另⼀个变了。怎么⽤上边的道理来解释这个诡异的不同呢?
⾸次把 [1, 2, 3] 看成⼀个物品。a = [1, 2, 3] 就相当于给这个物品上贴上 a 这个标签。⽽ b = a 就是给这个物品⼜贴上了⼀个 b 的标签。
第⼀种情况:
a = [4, 5, 6] 就相当于把 a 标签从 [1 ,2, 3] 上撕下来,贴到了 [4, 5, 6] 上。
在这个过程中,[1, 2, 3] 这个物品并没有消失。 b ⾃始⾄终都好好的贴在 [1, 2, 3] 上,既然这个 reference 也没有改变过。 b 的值⾃然不变。
第⼆种情况:
a[0], a[1], a[2] = 4, 5, 6 则是直接改变了 [1, 2, 3] 这个物品本⾝。把它内部的每⼀部分都重新改装了⼀下。内部改装完毕后,[1, 2, 3]本⾝变成了 [4, 5, 6]。
⽽在此过程当中,a 和 b 都没有动,他们还贴在那个物品上。因此⾃然 a b 的值都变成了 [4, 5, 6]。
搞明⽩这个之后就要问了,对于⼀个复杂对象的浅copy,在copy的时候到底发⽣了什么?
再看⼀段代码:
>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin ⾥边有三个元素:1, 2,[3, 4]
>>> cop1 = py(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False
#cop1 和 cop2 看上去相同,但已不再是同⼀个object
>>> origin[2][0] = "hey!"
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin内的⼦list [3, 4] 改掉了⼀个元素,观察 cop1 和 cop2
学过docker的⼈应该对镜像这个概念不陌⽣,我们可以把镜像的概念套⽤在copy上⾯。
概念图如下:
copy对于⼀个复杂对象的⼦对象并不会完全复制,什么是复杂对象的⼦对象呢?就⽐如序列⾥的嵌套序列,字典⾥的嵌套序列等都是复杂对象的⼦对象。对于⼦对象,python会把它当作⼀个公共镜像存储起来,所有对他的复制都被当成⼀个引⽤,所以说当其中⼀个引⽤将镜像改变了之后另⼀个引⽤使⽤镜像的时候镜像已经被改变了。
所以说看这⾥的origin[2],也就是 [3, 4] 这个 list。根据 shallow copy 的定义,在 cop1[2] 指向的是同
⼀个 list [3, 4]。那么,如果这⾥我们改变了这个 list,就会导致 origin 和 cop1 同时改变。这就是为什么上边 origin[2][0] = “hey!” 之后,cop1 也随之变成了 [1, 2, [‘hey!’, 4]]。
⽽deepcopy概念图如下:
deepcopy的时候会将复杂对象的每⼀层复制⼀个单独的个体出来。
这时候的 origin[2] 和 cop2[2] 虽然值都等于 [3, 4],但已经不是同⼀个 list了。即我们寻常意义上的复制。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论