Shallow copy instead of new object in Python
我试图在同一个类中创建一个类的新对象,但是它不是创建一个全新的对象,而是创建一个对我当前工作的同一个对象的新引用。
所以,如果我改变一个对象的值,它也会改变另一个对象的值——即使我可能有两个完全不同的对象。
使用
我的代码在这个特定的实现中是如何工作的?即使代码可能应该创建同一对象的新实例,它是否有理由创建该对象的浅副本?
这是一个稍微简化的代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | class Vec4(): def __init__(self, x = 0, y = 0, z = 0, w = 0): self.values = [x,y,z,w] def __str__(self): return str(self.values[0]) + ' ' + str(self.values[1]) + ' ' + str(self.values[2]) + ' ' + str(self.values[3]) def setValue(self, index, value): self.values[index] = value def scalar(self, vector): """returns the result of the scalar multiplication""" result = 0 for u in range(4): result += self.values[u] * vector.values[u] return result class Matrix4(): def __init__(self, row1 = Vec4(), row2 = Vec4(), row3 = Vec4(), row4 = Vec4()): self.m_values = [row1,row2,row3,row4] self.trans_values = [Vec4(),Vec4(),Vec4(),Vec4()] self.set_transp_matrix() def __str__(self): return self.m_values[0].__str__() + ' ' + self.m_values[1].__str__() + ' ' + self.m_values[2].__str__() + ' ' + self.m_values[3].__str__() def setIdentity(self): identity = Matrix4(Vec4(1,0,0,0), Vec4(0,1,0,0), Vec4(0,0,1,0), Vec4(0,0,0,1)) for i in range(4): for j in range(4): self.m_values[i].values[j] = identity.m_values[i].values[j] def set_transp_matrix(self): for t in range(4): for s in range(4): self.trans_values[t].values[s] = self.m_values[s].values[t] def get_trans_matrix(self): return self.trans_values[0].__str__() + ' ' + self.trans_values[1].__str__() + ' ' + self.trans_values[2].__str__() + ' ' + self.trans_values[3].__str__() def mulM(self, m): print(self," ") matrixResult = Matrix4() print(matrixResult," ") for row in range(4): # rows of self for element in range(4): value = self.m_values[row].scalar(m.trans_values[element]) matrixResult.m_values[row].setValue(element, value) return matrixResult class ScaleMatrix(Matrix4): def __init__(self, m_scale = Vec4(1,1,1), *args, **kwargs): super(ScaleMatrix, self).__init__(*args, **kwargs) self.m_scale = m_scale self.update() def getScale(self): """Returns the scale vector, only x, y and z are relevant""" return self.m_scale def setScale(self, v): """Sets the scale vector, only x, y and z are relevant""" self.m_scale = v self.update() def update(self): """Calculates the scale matrix""" self.setIdentity() for i in range(3): self.m_values[i].values[i] = self.getScale().values[i] return self if __name__ =="__main__": #Simple Constructor and Print a = Vec4(1,2,3,4) b = Vec4(5,6,7,8) c = Vec4(9,10,11,12) d = Vec4(13,14,15,16) A = Matrix4(a, b, c, d) D = ScaleMatrix() D.setScale(Vec4(3, 4, 5, 1)) print(D.mulM(A)) |
问题出在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 3 0 0 0 0 4 0 0 0 0 5 0 0 0 0 1 3 0 0 0 0 4 0 0 0 0 5 0 0 0 0 1 3 6 51 672 20 64 508 6688 45 140 1170 15340 13 40 334 4396 |
所以第二个矩阵不应该等于第一个矩阵。但是,如果我创建一个普通的
Python V.3.4.4
定义函数时,python计算默认参数。定义时:
1 2 | class Matrix4(): def __init__(self, row1 = Vec4() ... |
您创建一个
第一次创建
然后你有:
1 | self.m_values = [row1,row2,row3,row4] |
所以这个实例现在由self.m_值[0]引用。
稍后,在
1 2 | for i in range(3): self.m_values[i].values[i] = self.getScale().values[i] |
下次不带参数调用
当使用空列表作为默认参数时,您有类似的行为,请参见"最小惊讶"和可变默认参数。.
避免这个问题的通常方法是避免使用可变对象作为默认参数。你可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Matrix4(): def __init__(self, row1 = None, row2 = None, row3 = None, row4 = None): if row1 is None: row1 = Vec4() if row2 is None: row2 = Vec4() if row3 is None: row3 = Vec4() if row4 is None: row4 = Vec4() self.m_values = [row1,row2,row3,row4] self.trans_values = [Vec4(),Vec4(),Vec4(),Vec4()] self.set_transp_matrix() |
其输出为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 3 0 0 0 0 4 0 0 0 0 5 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 6 9 12 20 24 28 32 45 50 55 60 13 14 15 16 |
查看python构造函数和默认值以及"最小惊异"以及您的问题与上述链接问题中描述的问题基本相似的可变默认参数。
另外,在声明类时,使其成为
1 2 | class Vec4(object): class Matrix4(object): |
最后,要解决特定问题,请删除初始值设定项的默认值。例如,执行如下操作:
1 2 3 4 5 6 7 8 9 10 11 | class Matrix4(): def __init__(self, row1=None, row2=None, row3=None, row4=None): if row1 is None: row1 = Vec4() if row2 is None: row2 = Vec4() if row3 is None: row3 = Vec4() if row4 is None: row4 = Vec4() self.m_values = [row1,row2,row3,row4] |
同样适用于
1 2 | class ScaleMatrix(Matrix4): def __init__(self, m_scale = Vec4(1,1,1), *args, **kwargs): |