赋值、浅拷贝、深拷贝方法集合在文末。
输出结果
前两列为构成边的节点,第三列为该边的权重。
遇到的问题及解决方法
先贴一开始犯错的代码。
这个函数实现的功能是输入一个由边组成的二维列表,输出所有不重复的边以及权重(重复次数)。用于后续的社会网络分析与指标计算,因为是合作网络,所以不存在方向关系。
生成边列表的方法:生成边列表edges_list。
def get_weighted_links(edges_list):
''' edges_list:是包含边的列表,[[节点1,节点2],...],边可以重复,重复次数记为权重
返回值:带权重的边列表,[[节点1,节点2,weight],...]
'''
weighted_links = [] # 作为返回值
exist_links = [] # 记录当前已经出现过的边
for link in edges_list:
if link not in exist_links:
exist_links.append(link)
link.append(1) # 新边weight置1
weighted_links.append(link)
else:
# exsit_links和weighted_links里的列表顺序是一样的
index = exist_links.index(link)
# 已存在的边,weight + 1
weighted_links[index][2] = weighted_links[index][2] + 1
return weighted_links
调用这个函数,把输出存入csv之后,发现没有像预期的重复的边被合并并记录为权重,而是所有的边原封不动的输出了而且权重全部是1。
这就意味着函数执行过程中没有进入过else,也就是说所有新输入的边都不存在在exist_links中。这很奇怪,于是我输出了一下exist_links的前面几项。
print(exist_links[0:5])
结果发现exist_links里的link竟然也被append加了权重?
[['ANDREW-SMITH R', 'KATER P', 1], ['ANIMAS CORP.', 'LOCKHEED MARTIN CORP.', 1], ['ANIMAS CORP.', 'APG KK', 1], ['ANIMAS CORP.', 'ASEPSIS INC.', 1], ['ANIMAS CORP.', 'AQUA CLEAR INDUSTRIES LLC', 1]]
我又在调用结束时输出了一下原始输入links,也被改了。
[['1 A LIFESAFER INC', 'MONITECH INC', 1], ['3D HYRLIFTAR AB', 'BEIJING ZXWORLD CORP.', 1], ['3M CO', 'HONDA MOTOR CO. LTD.(HONDA GIKEN KOGYO KK)', 1], ['7188501 CANADA INC', 'HYDROGENICS CORP.', 1], ['A. RAYMOND GROUP', 'BOVILLE DANIEL', 1]]
我意识到问题可能出在对link的append操作上。即上面代码中的这一部分:
# 相同函数部分省略
exist_links.append(link)
link.append(1)
weighted_links.append(link)
# 相同函数部分省略
事实上,无论是原始links存储的,还是给exist_links或是csv_links传入的,都是每个link首地址的指针,因此,不管是什么操作顺序,一旦link被改变,这些列表中的值都会一起改变。
这都是列表浅拷贝的锅。
列表操作中,同样是浅拷贝的其他操作见文末。这里言归正传。
因此我又重新生成了一个新列表,进行了赋值。这次应该不会改变原列表了吧?
# 相同函数部分省略
exist_links.append(link)
_link = link
_link.append(1)
weighted_links.append(_link)
# 相同函数部分省略
结果发现,不对,还是改变了。(感觉事情突然严重了)。难道赋值操作存的也是指针?
不,赋值操作甚至谈不上拷贝,就是直接引用。此时两个列表是等价的,修改其中任何一个都会影响另一个。
因此在我的函数里,如果一定想要用这种操作顺序,代码就要使用列表的深拷贝。所谓深拷贝就是会重新开辟一块存储空间给新变量,这个时候两个列表就互不影响。深拷贝使用copy模块中的deepcopy()方法实现。
from copy import deepcopy
# 相同函数部分省略
exist_links.append(link)
# 不能直接link.append(1),exsit_link里的link也会变
_link = deepcopy(link) # 深拷贝
_link.append(1)
weighted_links.append(_link)
# 相同函数部分省略
这样修改之后,就可以实现函数的预期功能了。
还有另一种不使用深拷贝的改法。给exist_links存的link切片,links改变无所谓。
# 相同函数部分省略
link.append(1)
weighted_links.append(link)
exist_links.append(link[:2]) # 只取link前两项
# 相同函数部分省略
完整能正常实现功能的代码如下。最好还是用deepcopy,不然会把输入的内容给改了,比较危险。
def get_weighted_links(edges_list):
''' edges_list:是包含边列表的矩阵,[[节点1,节点2],...],边可以重复,重复次数记为权重
返回值:带权重的边列表,[[节点1,节点2,weight],...]
'''
weighted_links = []
exist_links = []
for link in edges_list:
if link not in exist_links:
exist_links.append(link)
_link = deepcopy(link) # 深拷贝
_link.append(1)
weighted_links.append(_link)
else:
index = exist_links.index(link)
weighted_links[index][2] = weighted_links[index][2] + 1
return weighted_links
赋值
赋值是非拷贝方法。赋值只是对另一个列表的引用,两个列表等价。
list_test = [1,2,3,4,5,6]
l1 = list_test
Python中不明确区分赋值和引用,一般对静态变量的传递为赋值(字符串,整数,元组),对动态变量的传递为引用(列表,字典)。
浅拷贝操作集合。
浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用,因此当原对象发生变化的时候,拷贝对象也跟着变化;深拷贝是拷贝多层,每一级别的数据都会拷贝出来,更改原对象,拷贝对象不会发生变化。
简单来说:浅拷贝相关的两个列表相互影响,对其中一个修改,另一个同时被修改。
import copy
# 1.切片
l2 = list_test[1:3]
# 2.list()方法
l3 = list(list_test)
# 3.循环调用赋值
l4 = [i for i in [i for i in L0]]
# 4. extend方法
l5 = []
l5.extend(list_test)
# 5.append方法
l6 = l4.append(list_test)
# 6.copy模块中的copy()方法
l7 = copy(list_test)
深拷贝
给变量开辟新存储空间,两个列表互不影响。
from copy import deepcopy
l8 = deepcopy(list_test)