当前位置: 首页 > 后端技术 > Python

Python列表二三事

时间:2023-03-26 17:15:46 Python

Python的列表是一个有序的集合,它对里面的元素类型没有要求,几乎所有的东西都可以放在一个列表里。这里讨论列表的四种常见操作:如何从列表中删除元素;列表的索引和切片是深拷贝还是浅拷贝;两个列表的交集、并集、差集和对称差集;列表的排序方法。1.删??除列表中的一个元素要删除列表中的一个元素,可以使用del和remove。del是按下标删除一个元素,remove是按值删除一个元素。只能删除匹配某个值的第一个元素。list1=['a','b','hello','world','hello']#根据下标删除元素dellist1[2]print(list1)['a','b','world','hello']list1=['a','b','hello','world','hello']list1.remove('hello')#按值删除元素,只删除第一个匹配某某的元素value可以删除一个元素print(list1)['a','b','world','hello']如果要删除列表中的所有“hello”,不能使用如下所示的for循环,否则如果列表中连续出现“hello”则不会被删除。list1=['a','b','hello','world','c','hello','hello','d']foriinlist1:ifi=='hello':list1.remove(i)print(list1)['a','b','world','c','hello','d']从结果来看,在for循环中,当remove删除一个匹配元素后,i已经指向了下一个元素(这和C语言中vector的迭代器是一样的),所以如果遇到连续两个“hello”,i就跳过第二个“hello”。验证如下,remove后i的值发生了变化:list1=['a','b','hello','world','c','hello','hello','d']foriinlist1:print('currentitem:'+i)ifi=='hello':list1.remove(i)print('afterremove:'+i)print(list1)currentitem:currentitem:bcurrentitem:helloafterremove:hellocurrentitem:ccurrentitem:helloafterremove:hellocurrentitem:d['a','b复制代码','world','c','hello','d']如果需要删除列表中所有匹配的元素,可以对列表进行深拷贝进行遍历,同时使用原始列表进行删除元素,例子如下:fromcopyimportdeepcopylist1=['a','b','hello','world','c','hello','hello','d']list2=deepcopy(list1)foriinlist2:print('currentitem:'+i)ifi=='hello':list1.remove(i)print('afterremove:'+i)print(list1)currentitem:currentitem:bcurrentitem:helloafterremove:hellocurrentitem:worldcurrentitem:ccurrentitem:helloafterremove:hellocurrentitem:helloafterremove:hellocurrentitem:d['a',b','world','c','d']2.列表的切片和索引是深拷贝还是浅拷贝?上面的例子提到了列表深拷贝,那么list的slice和index到底是深拷贝还是浅拷贝?先看list的index和slice的基本用法:Python中list的index可以是负数,负数就是倒序,倒序从-1开始#下标index和slicelist1=['a','b','c','d']print(list1[0])print(list1[1:3])print(list1[-4:-3])a['b','c']['a']list索引或分片是深拷贝还是浅拷贝?这里需要用到一个id方法,可以给出对象的内存地址。Python中复制列表实际上就是复制列表的引用,原对象和新对象会指向同一个内存地址。更改其中一个列表对象中的元素值,另一个也会更改。如下图,list1和list2其实指向同一个内存地址,所以一旦list2中某个元素的值改变了,list1也会随之改变。list1=['a','b','c']list2=list1print(id(list1))print(id(list2))list2[1]='d'print(list1)140356459153200140356459153200['a','d','c']希望list1和list2彼此无关。一种解决方法是用slice方法复制原来的对象,这样得到的list2的内存地址确实不一样。改变list2中元素的值,list1不会改变。#Copylist1usingslicemethod=['a','b','c']list2=list1[:]print(id(list1))print(id(list2))list2[1]='d'print(list1)140356987974432140356459153040['a','b','c']但是一切都会无忧吗?如果列表中的对象是一个复杂的结构,比如列表,那么按分片复制会不会有问题?list1=[['a','b'],['c','d']]list2=list1[:]print(id(list1))print(id(list2))list2[1][1]='x'print(list1)140356987975872140356458496720[['a','b'],['c','x']]如果遇到嵌套列表(二维数组),即使切片的方法是用于复制list2,修改list2中的元素,list1依然会被改变。因为list中的元素,比如list1[0],是一个list,一个object,一个reference,如果查看一下两者的内存地址,会发现其实是一样的。list1=[['a','b'],['c','d']]list2=list1[:]print(id(list1[0]))print(id(list2[0]))140356717561408140356717561408因此,当列表中有对象时,切片后修改元素会改变原列表中的值。安全的方法是使用深拷贝。fromcopyimportdeepcopylist1=[['a','b'],['c','d']]list2=deepcopy(list1)print('列表内存地址:')print(id(list1))print(id(list2))print('list[0]的内存地址:')print(id(list1[0]))print(id(list2[0]))list2[1][1]='x'print(list1)list的内存地址:140356987985824140356987984384list[0]的内存地址:140356459155120140356451242944[['a','b'],['c','d']]3.交集,并集,差集,symmetryoflistDifferenceset这也是一个比较常见的问题。给定两个列表,需要它们的交集、并集、差集和对称差集。这里给出了几种方法,并比较了性能。两个列表如下:list1=['hello','world','day','night','world']list2=['day','hello','spring']首先求交集,即find返回同时出现在list1和list2中的元素。这里给出三种写法,前两种是借助set实现的(推荐),后一种是链表遍历法。借助set方法,如果原列表中存在多个相同的元素,则不会保留多份,也不再保留列表中元素的顺序。#交集list3=list(set(list1)&set(list2))print(list3)list4=list(set(list1).intersection(set(list2)))print(list4)list5=[xforxinlist1ifxinlist2]print(list5)['hello','day']['hello','day']['hello','day']查找列表并集,即出现在list1或list2中的元素.#Unionsetlist3=list(set(list1)|set(list2))print(list3)list4=list(set(list1).union(set(list2)))print(list4)list5=list(set(list1+list2))print(list5)['night','day','spring','hello','world']['night','day','spring','hello','world']['day','spring','night','hello','world']求list的差异集,即出现在list1中但没有出现在list2中的元素#differencesetlist3=list(set(list1).difference(set(list2)))print(list3)list4=list(set(list1)-(set(list2)))print(list4)#Donotaskforuniqueorderlist5=[xforxinlist1ifxnotinlist2]print(list5)['night','world']['night','world']['world','night','world']求list的对称差集,只属于list1的元素和只属于list2的元素#对称差集list3=list(set(list1).symmetric_difference(set(list2)))print(list3)list4=list(set(list1)^(set(list2)))print(list4)#不寻找唯一顺序list5=[xforxinlist1ifxnotinlist2]+[xforxinlist2ifxnotinlist1]print(list5)['night','world','spring']['night','world','spring']['world','night','world','spring']在性能上,因为set内部有哈希表,比只处理list要高很多,set两种写法的性能差别不大。这是一个小实验。list1和list2都是包含100,000个数字的列表。用不同的方法解决它们的交叉点,并用时间来计算它们。在这个数量级上,只使用list方法性能较慢,所以如果不要求结果保留所有元素并保持原来的顺序,借用set是更推荐的方法。importrandomlist1=[]list2=[]foriinrange(100000):n=random.randint(0,100000)list1.append(n)m=random.randint(5000,105000)list2.append(m)%%time#Intersection1list3=list(set(list1)&set(list2))CPUtimes:user26.4ms,sys:1.86ms,total:28.2msWalltime:27.6ms%%time#Intersection2list4=list(set(list1).intersection(set(list2)))CPUtimes:user33.5ms,sys:1.17ms,total:34.7msWalltime:34ms%%time#Intersection3list5=[xforxinlist1ifxinlist2]CPU时间:用户2分钟20秒,系统:243毫秒,总计:2分钟20秒墙时间:2分钟20秒4。列表的排序操作列表的排序方法可以使用内置的sort和sorted,sorted有返回值,返回排序后的列表;sort是改变list的序列本身,没有返回值。排序方法list1=[5,2,3,1,4]list2=sorted(list1)print(list2)[1,2,3,4,5]排序方法list1=[5,2,3,1,4]list1.sort()print(list1)[1,2,3,4,5]排序时也可以通过key参数指定一个函数来计算要比较的值,这个函数会在每次元素比较之前被调用,因此可以通过指定键对复杂对象列表进行排序。比如按某个组件排序,按某个组件的长度排序等等。list1=[[1,'c','hello'],[2,'a','morning'],[3,'a','cat']]#按照元素中的某个成分对list1进行排序。sort(key=lambdax:x[1])print(list1)[[2,'a','morning'],[3,'a','cat'],[1,'c','你好']]#排序list1.sort(key=lambdax:len(x[2]))print(list1)[[3,'a','cat'],[1,'c','hello'],[2,'a','morning']]注:排序结果稳定。当keykey相同时,列表中最先出现的元素也排在排序结果的前面。如果列表中的元素是某个类的对象,还可以通过itemgetter和attrgetter获取元素或对象的属性,然后对它们进行排序。例子如下,如果列表本身的元素可以通过下标索引(比如嵌套列表),可以使用itemgetter获取组件。fromoperatorimportitemgetterlist1=[[1,'c','hello'],[2,'a','morning'],[3,'b','cat']]#可以使用下标索引例如,排序list1.sort(key=itemgetter(1))print(list1)[[1,'a','morning'],[3,'b','cat'],[1,'c','hello']]如果列表是一个复杂的类对象,可以使用attrgetter根据属性名获取属性的值,并以此排序。例如,首先创建一个Person对象列表:(self.name,self.age,self.work))list1=[Person('赵赵',45,'月亮中学'),Person('丽丽',20,'万向电子厂'),Person('旺旺',35,'万能电子厂')]然后根据Person的年龄属性排序fromoperatorimportattrgetter#sortanattributeoftheobjectlist2=[Person('赵赵',45,'月亮中学'),Person('李丽',20,'宇宙电子厂'),Person('旺旺',35,'宇宙电子厂')]list2.sort(key=attrgetter('age'))print(list2)[('丽丽',20,'万向电子厂'),('旺旺',35,'万向电子厂'),('赵赵',45,'月亮中学')]itemgetter和attrgetter比较方便最重要的是支持多级排序,也就是可以传入多个keys,先按第一个key排序,如果第一个key相同,再按第二个key排序。#先按第0个元素排序,再按第1个元素排序list1=[[1,'c','hello'],[1,'a','morning'],[3,'b','cat']]list1.sort(key=itemgetter(0,1))print(list1)[[1,'a','morning'],[1,'c','hello'],[3,'b','cat']]#先按工作排序,再按年龄排序list2=[Person('赵赵',45,'月亮中学'),Person('李李',20,'万向电子厂'),Person('旺旺',35,'万向电子厂')]list2.sort(key=attrgetter('work','age'))print(list2)[('李丽',20,'万向电子factory'),('汪汪',35,'万向电子厂'),('赵赵',45,'月亮中学')]总结本文讨论list的四种常用操作:1.如何从list2.当list为复杂结构对象时,切片和索引不是深拷贝;3、用set解决两个列表的交、并、差、对称差;4.列表方法的多重排序。我的Python版本>>>importsys>>>print(sys.version)3.7.6(默认,2020年1月8日,13:42:34)[Clang4.0.1(tags/RELEASE_401/final)]