Python里list的复制问题

写代码的时候发现的……太傻了……
查了一下发现里面还有学问,尤其是对列表里嵌套列表要格外注意!

浅拷贝

意思是修改了原列表/复制列表里的值,另一个列表也会被改变。可以理解为它们的内存是同一块地方?只是给了一个新的指针指向那里。所以无论改哪个,另一个都会被修改。

什么时候会出现浅拷贝?

我可以总结为没有使用deepcopy()的全是浅拷贝吗?

  • python里列表list采用“=”赋值的时候
    当修改等号右边的原list时,新list也会改变。
>>> x = [1,2,3,4]
>>> y = x
>>> y.pop()
4
>>> y
[1, 2, 3]
>>> x
[1, 2, 3]
  • list采用“*”复制多个列表的时候,其实和上面相同,遇到嵌套列表的时候会出问题
>>> A = [[None,None]]
>>> B = A*3
>>> B[0][0]=0
>>> A
[[0, None]]
>>> B
[[0, None], [0, None], [0, None]]
=========== 通过id()查看内存地址
>>> id(B[0])
44594056L
>>> id(B[2])
44594056L
============= 看到A、B里面元素的内存是一样的!
>>> id(B[2][0])
31897360L
>>> id(B[2][1])
499518584L
>>> id(B[1][0])
31897360L
>>> id(B[1][1])
499518584L
>>> id(A[0][1])
499518584L
>>> id(A[0][0])
31897360L
>>> id(B[0])
44594056L
>>> id(A[0])
44594056L
==============疑问!!!!!! 这个A的地址是啥呀?为啥不是44560456L呢???
>>> id(A)
44595080L
>>> id(B)
44560456L
  • 采用copy()函数
    Python2.7里list没有这个内置函数,会报错,但是看到有人用,Python3应该有吧…
>>> z = x.copy()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'copy'

在没有嵌套的时候,或者字典嵌套列表的时候,copy()是可以复制字典不会被修改的。。那看来是list自己的问题。。

>>> a = {'x':11,'y':22}
>>> a.copy()
{'y': 22, 'x': 11}
>>> b = a.copy()
>>> b.popitem()
('y', 22)
>>> a
{'y': 22, 'x': 11}
>>> b
{'x': 11}
=========================
>>> a = {'x':[11, 22], 'y':[33,44]}
>>> b = a.copy()
>>> a['x']=13
>>> b
{'y': [33, 44], 'x': [11, 22]}
>>> a
{'y': [33, 44], 'x': 13}
>>> a['y']=[33,46]
>>> b
{'y': [33, 44], 'x': [11, 22]}
  • 用for循环生成列表:
old = [1,[1,2,3],3]
new = []
for i in range(len(old)):
    new.append(old[i])
print('Before:')
print(old)
print(new)
new[0] = 3
new[1][0] = 3
print('After:')
print(old)
print(new)

结果:

Before:
[1, [1, 2, 3], 3]
[1, [1, 2, 3], 3]
After:
[3, [3, 2, 3], 3]
[3, [3, 2, 3], 3]
  • 使用切片
old = [1,[1,2,3],3]
new = old[:]
print('Before:')
print(old)
print(new)
new[0] = 3
new[1][0] = 3
print('After:')
print(old)
print(new)

结果:

Before:
[1, [1, 2, 3], 3]
[1, [1, 2, 3], 3]
After:
[3, [3, 2, 3], 3]
[3, [3, 2, 3], 3]

解决方法

采用深复制的方法!

深复制

介绍

就是在内存里新开了一片地方存复制的数据,指针指的是不同的地址。

实现

  • 使用deepcopy()函数,需要引入copy包:
>>> import copy
>>> z = copy.deepcopy(x)
>>> z.pop()
3
>>> z
[1, 2]
>>> x
[1, 2, 3]
import copy
old = [1,[1,2,3],3]
new = copy.deepcopy(old)
print('Before:')
print(old)
print(new)
new[0] = 3
new[1][0] = 3
print('After:')
print(old)
print(new)

结果:

Before:
[1, [1, 2, 3], 3]
[1, [1, 2, 3], 3]
After:
[1, [1, 2, 3], 3]
[3, [3, 2, 3], 3]
  • 解决“*”的使用问题:使用新创建的列表为每个复制的对象赋值
>>> A = [None]
>>> B = [A*2 for i in range(3)]
>>> B
[[None, None], [None, None], [None, None]]
>>> A
[1]
>>> B[0][0]=2
>>> A
[1]
>>> B
[[2, None], [None, None], [None, None]]

注意*和for搭配使用!给我整的有点蒙了……对比下面3个栗子

>>> A[0]=1
>>> B
[[None, None], [None, None], [None, None]]
>>> A
[1]
>>> B[0][0]=1
>>> A
[1]
>>> B[0][0]=2
>>> A
[1]
>>> B  # 明显看到只修改了B的一个值,A的值并没有改变。
[[2, None], [None, None], [None, None]]
====================== 这个就是 上面提到过的嵌套列表用for是浅复制呀!
>>> old = [1,[1,2,3],3]
>>> new = [old for i in range(1)]
>>> new
[[1, [1, 2, 3], 3]]
>>> old
[1, [1, 2, 3], 3]
>>> new[0][0] = 3
>>> new
[[3, [1, 2, 3], 3]]
>>> old
[3, [1, 2, 3], 3]
======================== for和“*”一起就变成深复制了?
>>> old = [1,[1,2,3],3]
>>> new = [old for i in range(2)]
>>> new
[[1, [1, 2, 3], 3], [1, [1, 2, 3], 3]]
>>> new[0][0]
1
>>> new[0][0]=3
>>> old
[3, [1, 2, 3], 3]
>>> new = [old*1 for i in range(2)]
>>> new
[[3, [1, 2, 3], 3], [3, [1, 2, 3], 3]]
>>> new[0][0]=1
>>> new
[[1, [1, 2, 3], 3], [3, [1, 2, 3], 3]]
>>> old
[3, [1, 2, 3], 3]

https://www.cnblogs.com/Black-rainbow/p/9577029.html
https://www.cnblogs.com/yinghao-liu/p/8641236.html

原文地址:https://www.cnblogs.com/sweetsmartrange/p/12890477.html