Python 中的浅拷贝而不是新对象
Shallow copy instead of new object in Python
我正在尝试在同一个 class 中创建一个 class 的新对象,但是它没有创建一个全新的对象,而是简单地创建了对同一对象的新引用我'我目前在工作。
因此,如果我更改一个对象的值,它也会更改另一个对象的值 - 即使我可能应该有 2 个完全不同的对象。
使用 copy.deepcopy()
方法修复了引用的问题,但我想它的工作方式也应该有所不同。
我的代码在此特定实现中的行为如何?是否有理由创建同一对象的浅表副本,即使代码可能应该创建它的新实例?
这是一个稍微简化的代码片段:
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__() + '\n' + self.m_values[1].__str__() + '\n' + self.m_values[2].__str__() + '\n' + 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__() + '\n' + self.trans_values[1].__str__() + '\n' + self.trans_values[2].__str__() + '\n' + self.trans_values[3].__str__()
def mulM(self, m):
print(self, "\n")
matrixResult = Matrix4()
print(matrixResult, "\n")
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))
问题出在 class Matrix4
方法 mulM()
中,其中 matrixResult = Matrix4()
应该创建一个全新的 Matrix4()
实例(其中所有值Vec4()
应该是 0
) 而不是简单地复制 self
对象。 print
的输出显示如下:
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
所以第二个矩阵不应该等于第一个。
但是,如果我创建一个普通的 Matrix4()
对象而不是在上面代码片段的最后扩展 Matrix4()
的 ScaleMatrix()
对象,则不会出现此问题。
Python v.3.6.4
Python 在定义函数时评估默认参数。当你定义:
class Matrix4():
def __init__(self, row1 = Vec4() ...
您创建了一个 Vec4
实例,现在每次调用此 __init__
方法时都会将此实例用作默认值。
第一次创建 Matrix4
实例时,__init__
将执行,并且此实例被名称 row1
引用
那么你有:
self.m_values = [row1,row2,row3,row4]
所以这个实例现在被 self.m_values[0] 引用了。
稍后,在 ScaleMatrix.update
中,您更新此 Vec4
实例:
for i in range(3):
self.m_values[i].values[i] = self.getScale().values[i]
下次不带参数调用Matrix4.__init__
时,将使用默认值,这就是您刚刚更新的Vec4
。
当您使用空列表作为默认参数时,您有类似的行为,请参阅“Least Astonishment”和可变默认参数
.
避免这个问题的通常方法是避免使用可变对象作为默认参数。你可以这样做:
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()
输出为:
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 constructor and default value and "Least Astonishment" and the Mutable Default Argument 您的问题与上述链接问题中描述的问题基本相似。
此外,在声明 class 时,使其成为 object
的子 class:
class Vec4(object):
class Matrix4(object):
最后,要解决您的特定问题,请删除初始化程序的默认值。例如,做这样的事情:
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]
同样适用于 ScaleMatrix
参数的默认值 m_scale
:
class ScaleMatrix(Matrix4):
def __init__(self, m_scale = Vec4(1,1,1), *args, **kwargs):
我正在尝试在同一个 class 中创建一个 class 的新对象,但是它没有创建一个全新的对象,而是简单地创建了对同一对象的新引用我'我目前在工作。
因此,如果我更改一个对象的值,它也会更改另一个对象的值 - 即使我可能应该有 2 个完全不同的对象。
使用 copy.deepcopy()
方法修复了引用的问题,但我想它的工作方式也应该有所不同。
我的代码在此特定实现中的行为如何?是否有理由创建同一对象的浅表副本,即使代码可能应该创建它的新实例?
这是一个稍微简化的代码片段:
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__() + '\n' + self.m_values[1].__str__() + '\n' + self.m_values[2].__str__() + '\n' + 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__() + '\n' + self.trans_values[1].__str__() + '\n' + self.trans_values[2].__str__() + '\n' + self.trans_values[3].__str__()
def mulM(self, m):
print(self, "\n")
matrixResult = Matrix4()
print(matrixResult, "\n")
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))
问题出在 class Matrix4
方法 mulM()
中,其中 matrixResult = Matrix4()
应该创建一个全新的 Matrix4()
实例(其中所有值Vec4()
应该是 0
) 而不是简单地复制 self
对象。 print
的输出显示如下:
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
所以第二个矩阵不应该等于第一个。
但是,如果我创建一个普通的 Matrix4()
对象而不是在上面代码片段的最后扩展 Matrix4()
的 ScaleMatrix()
对象,则不会出现此问题。
Python v.3.6.4
Python 在定义函数时评估默认参数。当你定义:
class Matrix4():
def __init__(self, row1 = Vec4() ...
您创建了一个 Vec4
实例,现在每次调用此 __init__
方法时都会将此实例用作默认值。
第一次创建 Matrix4
实例时,__init__
将执行,并且此实例被名称 row1
那么你有:
self.m_values = [row1,row2,row3,row4]
所以这个实例现在被 self.m_values[0] 引用了。
稍后,在 ScaleMatrix.update
中,您更新此 Vec4
实例:
for i in range(3):
self.m_values[i].values[i] = self.getScale().values[i]
下次不带参数调用Matrix4.__init__
时,将使用默认值,这就是您刚刚更新的Vec4
。
当您使用空列表作为默认参数时,您有类似的行为,请参阅“Least Astonishment”和可变默认参数 .
避免这个问题的通常方法是避免使用可变对象作为默认参数。你可以这样做:
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()
输出为:
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 constructor and default value and "Least Astonishment" and the Mutable Default Argument 您的问题与上述链接问题中描述的问题基本相似。
此外,在声明 class 时,使其成为 object
的子 class:
class Vec4(object):
class Matrix4(object):
最后,要解决您的特定问题,请删除初始化程序的默认值。例如,做这样的事情:
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]
同样适用于 ScaleMatrix
参数的默认值 m_scale
:
class ScaleMatrix(Matrix4):
def __init__(self, m_scale = Vec4(1,1,1), *args, **kwargs):