最近连续两次遇到因为深浅复制而导致的问题(真是记吃不记打),所以觉得有必要记录一下(长长记性)。
- 第一次是在Python中:做用例统计处理时遇到的,使用python提供的 copy.deepcopy(object)深复制就可以解决,
- 第二次是在JavaScript中:前端做复制UI操作步骤时遇到的,修改复制的步骤,保存后发现原步骤也被修改了,但很遗憾 js中并没有提供深层复制的处理方法,所以需要写一个专门的方法进行处理
深浅复制,并非只存在于某一种语言,而是所有带有可变类型变量的语言都会有的问题。深浅复制的问题起源于变量的可变、不可变属性。以Python为例:
- 不可变类型:数字、字符串、tuple元组,这类变量一旦被重新赋值,将会创建新的对象、变量对应的内存地址就会发生改变
- 可变类型:dict、list,这类变量在进行内部元素的添加、删除、修改时,并不会影响变量本身的内存地址
假设变量的名字和值之间有一根线(指针)进行关联,对于数字、字符串、元组等不可变类型的变量来说,每次改变变量的值,这根关联的线就会移动到新的对象(新的内存地址)上;而对于dict、list这类可变类型的变量,这根线关联的值只存储了这个可变变量的内存地址(可以想象只给{}/[]分配了存储空间),真正存储的变量中元素的在其它的存储空间,dict通过链的方式将内部的元素关联起来,因此当新增、删除、修改元素时,并不影响变量的存储空间。以上仅基于自己的理解,有兴趣可以看看数据结构相关的资料。
总结一下,所谓可变、不可变是基于在不改变变量内存地址的情况下,变量内容是否可以进行改变来进行区别。
Ps:若元组(不可变)中,包含了dict、list等可变类型元素,在改变dict/list中元素时,元组内容已经发生改变。所以,元组是可变?还是不可变呢?
现在,再说回深浅复制
浅复制:是指仅复制了可变类型的变量地址(就像只记你家的门牌号)
深复制:是将可变类型变量中的每一项元素都进行了独立的复制(就像把你家里的每个人都克隆了一份一模一样的,再发了一个新房子,又是新的一家人)
- 当向可变类型变量中添加一个元素时,浅复制的对象由于只是复制了内存地址,任何引用内存地址的变量都会受到影响;而深复制与原变量已经没有关联,不受任何影响
第一次Python深浅复制实例源码:
import copy
def get_user_case_count(proj_id,startdate=None,enddate=None):
case_status_dict = {0: '新建', 1: '成功', 2: '失败', 5: '挂起', 9: '废弃'}
rs = select('SELECT username,status,count(*) FROM XXX GROUP BY username,status')
data = [] # 存放最终的统计数据列表
tmp_dict = {} # 临时存放每一个用户的用例统计数据
for row in rs:
if len(tmp_dict) == 0:
tmp_dict['username'] = row[0]
elif tmp_dict['username']!=row[0]:
# 此处使用深复制,并清空dict下次使用;否则会将上一次的结果带入下一次
data.append(copy.deepcopy((tmp_dict)))
tmp_dict.clear()
tmp_dict['username'] = row[0]
tmp_dict[case_status_dict[row[1]]] = row[2]
data.append(tmp_dict)
return data
第二次JS深浅复制实例源码:
function copySteps(){
// 获取选中的行进行复制,追加到表格末尾
row = table_steps.datagrid('getSelected');
if(row){
// 此处,若直接使用row进行追加,则会导致选中行数据同时改变
table_steps.datagrid('appendRow',copyDeep(row));
}else{
$.messager.alert("提示","请先选中需要复制的行!");
}
}
function copyDeep(parent, child) {
child = child || {};
for(var i in parent) {
if(parent.hasOwnProperty(i)) {
if(parent[i] === null){
child[i] = parent[i];
}else if(typeof parent[i] === "object") {
child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {};
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
}