最近在备课一个AI的俄罗斯方块项目,在模拟填充加权计算环节,遇到了一个列表变量赋值、浅拷贝、深拷贝的问题,方便理解,以下对场景做简化。
class A:
def __init__(self, data = None):
if data == None:
data = []
self.data = data
def add(self, var):
self.data.append(var)
obj1 = A([1, 2, 3])
print(obj1.data) # 输出:[1, 2, 3]
obj2 = A(obj1.data)
obj2.add(4) # 对obj2.data添加元素
print(obj2.data) # 输出 [1, 2, 3, 4]
print(obj1.data) # 输出 [1, 2, 3, 4]
以上是原始场景,bug出现在最后一行,当对obj2添加元素后,obj1.data 也会跟着变化,但初始目的是只传参,不想改变 obj.data 的原始值。
问题出现的原因是 obj2 = A(obj1.data) 实例化obj2时,其实是将 obj1.data 赋值给__init__的参数data,相当于obj2.data = obj1.data。
这样就出现了变量赋值引用,两个值其实是指向同一个地址的问题。
class A:
def __init__(self, data = None):
if data == None:
data = []
self.data = data
def add(self, var):
self.data.append(var)
obj1 = A([1, 2, 3])
print(obj1.data) # 输出:[1, 2, 3]
obj2 = A(obj1.data.copy()) # 或者 obj2 = A(list(obj1.data))
obj2.add(4) # 对obj2.data添加元素
print(obj2.data) # 输出 [1, 2, 3, 4]
print(obj1.data) # 输出 [1, 2, 3, 4]
以上是改进方案,用浅拷贝的方式,使其指向不同地址,看上去像解决了问题。但之后我又遇到了,一个二维列表修改元素的问题。
class A:
def __init__(self, data=None):
if data == None:
data = []
self.data = data
def setVal(self, x, y, var):
self.data[x][y] = var
obj1 = A([[0] * 3 for _ in range(3)])
print(obj1.data) #输出 [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
obj2 = A(obj1.data)
obj2.setVal(1, 1, 1)
print(obj2.data) #输出 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
print(obj1.data) #输出 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
可以看到,在对 obj2.setVal() 之后,对 obj1.data 也会产生影响,所以浅拷贝只复制父对象就不够了,得用深拷贝才能解决,代码如下。
import copy
class A:
def __init__(self, data=None):
if data == None:
data = []
self.data = data
def setVal(self, x, y, var):
self.data[x][y] = var
obj1 = A([[0] * 3 for _ in range(3)])
print(obj1.data) #输出 [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
obj2 = A(copy.deepcopy(obj1.data)) #深拷贝
obj2.setVal(1, 1, 1)
print(obj2.data) #输出 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
print(obj1.data) #输出 [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
Python这个语言挺有意思,一个拷贝的事整这么花哨,得处处小心。
解决方案参考:https://www.runoob.com/w3cnote/python-understanding-dict-copy-shallow-or-deep.html
本文为 陈华 原创,欢迎转载,但请注明出处:http://www.chenhuax.com/read/195
- 上一篇:
- 垂杨柳医院驾驶证体检和网上换证经历