SciPy稀疏矩阵传参执行+=未修改原对象原因解析
SciPy稀疏矩阵与NumPy数组执行
+=操作时的原对象修改行为差异 问题现象
使用SciPy与NumPy进行开发时可以观察到如下不一致行为:
- 定义内部仅执行
m += m操作的函数func - 分别向函数传入2阶单位稀疏矩阵
a = sparse.identity(2)、NumPy数组b = np.array([1, 2]) - 调用
func后,全局作用域下的稀疏矩阵a未发生任何修改,NumPy数组b却被修改为原值的2倍
复现代码
from scipy import sparse import numpy as np def func(m): m += m a = sparse.identity(2) b = np.array([1, 2]) print(a.todense()) # [[1,0],[0,1]] func(a) print(a.todense()) # 仍为[[1,0],[0,1]],无修改 print(b) # [1, 2] func(b) print(b) # 变为[2, 4],原对象被修改
差异产生的核心原因
该差异和Python函数传参机制无关——Python对所有对象统一采用传对象引用的规则,形参和实参初始时会指向同一个内存对象。行为不一致的根源是两类对象对原地自增运算符+=的底层实现逻辑完全不同:
- NumPy ndarray的
+=是严格的原地操作:运算时直接在原数组的内存空间上修改元素值,全程不会生成新的数组对象。因此函数内对形参m的修改会直接作用在全局变量b指向的同一块内存上,最终表现为原对象被修改。 - SciPy绝大多数格式的稀疏矩阵(本例中
sparse.identity默认返回CSR格式稀疏矩阵)没有为+=实现真正的原地修改逻辑:执行m += m时,底层会先计算m + m的结果生成一个全新的稀疏矩阵对象,再将局部变量m重新绑定到这个新对象上。整个过程中全局变量a始终指向最初的稀疏矩阵对象,没有任何修改操作作用到它身上,因此值不会发生变化。
可以通过
id()函数快速验证该逻辑:在func函数内m += m语句的前后分别打印id(m),传入NumPy数组时两次输出的id完全一致,说明是同一个对象;传入稀疏矩阵时两次id不同,说明局部变量已经指向了新生成的对象。
如果需要对稀疏矩阵实现类似NumPy数组的原地修改效果,可以直接操作稀疏矩阵存储非零值的属性,避免生成新对象:
def sparse_inplace_add(m): # 对CSR/CSC等基于data属性存储非零值的稀疏格式有效 m.data += m.data
内容的提问来源于stack exchange,提问作者user6745003
相关产品推荐
相关产品推荐

