之前没怎么接触过python,因为项目要用的python,所以开始学习python,说实话和java,c++比起来,python简单多了,但是由于是初次学习,在使用python遍历删除元素的时候遇到了一些坑,简单的记录下。
先说下应用场景:在进行安卓Apk的so加密保护中,需要从用户输入的一个so的list列表中,首先要判断用户输入的so是否在待加密Apk中存在,当然允许用户输入大小写有误,如果不存在则将该so从用户输入的list列表中删除,如果仅仅是大小写不匹配则认为该so是存在的,即需要修正so列表,且结果要反应到用户传入的so的list中。
因为结果要反应到用户传入的so的list中,那么很显然,我们应该对传入的list进行操作,而不能重新定一个list操作,然后返回。soLists为传入的用户输入的so列表,因为工作原因,只能贴出部分代码,代码如下:
existedSoList=[];
zipFileLists=ZipManager.getZipNameList(tempApkPath)
for file in zipFileLists:
if file.endswith('.so'):
soName=os.path.split(file)[-1]
existedSoList.append(soName)
#for so in soLists[:]:
for i in range(len(soLists)-1,-1,-1):#此处使用for in range,
flag=False
for existedSo in existedSoList:
if existedSo.lower() == soLists[i].lower():
flag=True
soLists[i] = existedSo #修正soLists
if not flag:
soLists.remove(soLists[i])#修正soLists
在上述代码中遍历方式使用的是for in range,而不是for in形式。 因为虽然使用for in可以做到安全的遍历删除,遍历拷贝的list,操作原始的list:代码如下:
for so in soLists[:]:
flag=False
for existedSo in existedSoList:
if existedSo.lower() == so.lower():
flag=True
so = existedSo #修正soLists
if not flag:
soLists.remove(so)#修正soLists
但是事实上,so = existedSo这句代码起不到期待的作用,因为为了保证遍历时的安全删除采用的是遍历拷贝的list,操作原始的list(如果不这么做则不能在遍历时删除元素,不然可能导致数组下标越界的错误),所以so = existedSo这个语句不会反应到原始的传入的so的list中,也即起不到修正大小写的作用。所以采用的是for in range形式倒序遍历删除同时可以修改原list中的元素的值。 因为是直接对原list进行操作,所以为了保证遍历的时候安全删除采用了倒序遍历的形式,即通过指定range函数的参数,range函数的定义如下:
def range(start=None, stop=None, step=None)
我们知道在c++/java中我们通常这么使用for循环
for(int i=0;i<list.size();i++) //正序访问元素 for (int i=list.size()-1;i >-1;i--) // 逆序访问元素
而在python中的range函数的start参数就是起始访问下标,stop就是结束访问下标,而step则是访问的步长,该值可以为正(正序访问元素),也可以为负(逆序访问元素),但是不能为0.如:
for i in range(len(list)-1,-1,-1):
等同于c++/java中的:
for (int i=list.size()-1;i >-1;i--) // 逆序访问元素
总结:如果仅仅只是在遍历的时候安全删除元素,可以采用
1遍历拷贝的list,操作原始的list:
for so in soLists[:]:
2采用for in range 的形式倒序遍历。
for i in range(len(list)-1,-1,-1):
如果仅仅只是在遍历的时候安全删除元素,推荐第一种方式,如果不仅要保证遍历时安全删除,同时要修改原list的元素的值,那个采用第二种方式。
文章评论
写的不错,的确是python新手容易遇到的问题,赞一个。
评论者头像来自Gravatar。
@马化腾 这名字太吊了,难道是腾讯的那个马化腾pony?