Python中创建类实例时出现浅拷贝而非新对象的问题排查
这是Python中非常典型的可变默认参数陷阱,不是浅拷贝的问题——根源出在你Matrix4类的__init__方法里的参数定义上。
让我们一步步拆解问题:
问题的核心:Python默认参数的初始化时机
你定义Matrix4的构造函数时,用了Vec4()作为默认参数:
def __init__(self, row1 = Vec4(), row2 = Vec4(), row3 = Vec4(), row4 = Vec4()): self.m_values = [row1,row2,row3,row4] # ...其他代码
在Python中,函数的默认参数是在函数被定义的时候就初始化完成的,而不是每次调用函数时重新创建。也就是说,这四个Vec4()实例,是在Matrix4类被加载到内存时就创建好了的“共享对象”——所有通过Matrix4()(不带任何参数)创建的实例,都会复用这四个Vec4对象。
你的代码里发生了什么?
当你创建ScaleMatrix实例D时:
ScaleMatrix.__init__调用了super().__init__(),也就是不带参数的Matrix4()构造函数,此时D的m_values引用了那四个全局共享的Vec4实例。- 接着
update()方法调用setIdentity()修改了这些Vec4的值,把它们改成了缩放矩阵的对角线值。
之后在mulM方法里执行matrixResult = Matrix4()时,这个新的Matrix4实例依然会复用那四个已经被D修改过的Vec4对象——所以你看到的matrixResult一开始就和D的初始状态一样,而不是预期的零矩阵。
而你直接创建Matrix4对象时(比如A = Matrix4(a,b,c,d)),因为传入了自定义的Vec4实例,没有用到默认参数,所以不会触发这个共享问题,看起来一切正常。
怎么解决这个问题?
正确的做法是把默认参数设为None,然后在构造函数内部创建新的Vec4实例,确保每次调用Matrix4()时都会生成全新的对象:
class Matrix4(): def __init__(self, row1=None, row2=None, row3=None, row4=None): # 仅当参数为None时,创建新的Vec4实例 self.m_values = [ row1 if row1 is not None else Vec4(), row2 if row2 is not None else Vec4(), row3 if row3 is not None else Vec4(), row4 if row4 is not None else Vec4() ] self.trans_values = [Vec4(), Vec4(), Vec4(), Vec4()] self.set_transp_matrix() # ...其他方法保持不变
这样无论你多少次调用Matrix4(),都会得到拥有独立Vec4实例的全新矩阵,不会再出现共享引用的问题。
额外提醒
这个陷阱不止出现在自定义类实例上——任何可变对象(比如列表、字典)作为默认参数时都会触发这个问题。比如def func(a=[]):,多次调用func()都会复用同一个列表,这是Python新手很容易踩的坑。
内容的提问来源于stack exchange,提问作者webFashion

