本文基于python3.11版本,部分方法或参数在早期版本可能不同,list(列表)是python基本数据类型之一,是可变对象,即修改后会改变对象的值,但该对象的地址不会发生变化 ,本身是一个容器,内部可以容纳任意类型的对象,并且是有序序列
- lst = [1, 2, 'ab']
- print(lst, id(lst)) # >>>[1, 2, 'ab'] 1681532193024
- lst[1] = 3
- print(lst, id(lst)) # >>>[1, 3, 'ab'] 1681532193024
list类的内置方法(不含魔术方法)中既有修改器(会改变原对象的值)也有纯函数(对原对象不产生任何影响),纯函数包括copy、count和index三个方法:
copy()是浅拷贝一个list对象,如果list内部容纳的是可变对象,则内部的可变对象仍然是和复制前共用一个地址,即改变这个可变对象也会改变原来list中的相应对象,若想避免这种情况,可以使用标准库copy中的deepcopy函数来深拷贝list对象
- lst1 = [[], 2]
- lst2 = lst1.copy()
- print(id(lst1) == id(lst2)) # False
- print(id(lst1[0]) == id(lst2[0])) # True
- lst2[0].append(1) # 改变lst2中的可变对象影响到了lst1
- print(lst1, lst2, seq=', ') # [[1], 2], [[1], 2]
- lst2[1] = 3 # 改变lst2不可变对象不会影响lst1
- print(lst1, lst2) # [[1], 2], [[1], 3]
count(value)内除了接受list的实例对象之外,还需要接受一个value参数,返回list容器内有多少对象的值与value相同,index(value, start, stop)还需接受一个value参数,另有两个可选参数start和stop,返回从start到stop第一个值为value的索引,(注意两点,一是stop索引不会被扫描到,只会扫描到stop-1索引,二是若没有找到value,则会抛出ValueError错误)若不填可选参数,则默认从表头扫描到表尾,默认情况下,这两种方法会把整个list序列扫描一遍
其它方法都会改变list本身的值,append(value)在列表尾部添加一个元素value,pop()在列表尾部弹出一个元素(为什么说弹出,因为这个方法还会返回被删除的对象),还有两个更加通用的方法,remove(value)移除第一个值为value的对象,insert(index, object)在index索引(支持负索引)处插入object对象,其它元素索引向后移一位,但注意,remove和insert方法速度可能很慢,看以下代码:
- from time import time
-
- T = 10000
- lst1, lst2 = [1] * T, [1] * T
- start = time()
- for _ in range(T):
- lst1.pop()
- middle = time()
- print(middle - start) # 耗时0.00s
- for _ in range(T):
- lst2.remove(1)
- print(time() - middle) # 耗时0.09747028350830078s
-
- # remove是删除第一个遍历到的value,因此lst2的remove实际上每次移除的是第一个元素而不是最后一
- # 个,这是一个非常耗时的过程,若把T改为100000,第一个循环仍然快速完成,第二个循环会消耗非常
- # 长的时间
-
- # list如果不是被移除最后一个元素或在末端添加一个元素,那么相当于重新拷贝一遍插入或移除的索引
- # 后的list,以保证list是一段连续的序列,这样连续的序列会使得我们可以用如lst[index]的方法快速
- # 查询位于index索引处的值
extend(iterable)内接受一个可迭代对象,将这个可迭代对象内部的元素按顺序一个一个append到list的末端,reverse()用于反转列表,clear()用于清空列表,sort(key, reverse)使得列表内部元素按规则排序,key接受一个函数对象用于更改排序规则,reverse为布尔值,True表示按规则降序排列,False则反之,默认为False,即升序,下列代码块阐述参数key的用法:
- from random import randint, seed
-
- SEED = 1007
- seed(SEED) # 设置随机数种子,保证结果一致
- lst = [randint(1, 10000) for _ in range(10)]
- lst.sort()
- print(lst) # [528, 932, 2852, 2906, 3217, 3683, 5101, 8532, 9143, 9730]
- lst.sort(key=lambda x: -x)
- # lambda关键字接受一个形参,然后冒号后接函数返回值
- # lambda x: -x等价于 def f(x): return -x,为匿名函数
- print(lst) # [9730, 9143, 8532, 5101, 3683, 3217, 2906, 2852, 932, 528]
- # 也就是说key函数应接受一个参数,然后按照该返回值的大小进行升序排列(reverse=False时)
- # 对于整数x而言,x越大则-x越小,显然这样等价于降序排列,但元素不是整数则不一定
-
-
- lst = [[1, 2], [2, 3], [2, 6], [3, 4], [5, -10, -10], [5, -9], [5, -10]]
- lst.sort()
- print(lst) # [[1, 2], [2, 3], [2, 6], [3, 4], [5, -10], [5, -10, -10], [5, -9]]
- # list比较的规则是按顺序依此比较,直到出现一个更大或更小为止,若每个位置元素都相同则相等
- # 长度不同时,先比较大小,若直到更短的遍历完都相同,则短的比长的小
- # 我们想反过来,即从后往前遍历,更早出现更大值的list更大,则应写成如下形式
- lst.sort(key=lambda x: x[::-1])
- print(lst) # [[5, -10, -10], [5, -10], [5, -9], [1, 2], [2, 3], [3, 4], [2, 6]]
- # x[::-1]为切片操作,返回x的反转list,注意切片不会改变原对象
-
-
- # 如果我们想仅一步改变排序规则,比如我们判断两个字符全是数字的字符串x和y
- # 规则为x和y拼接起来与y和x拼接起来比较大小,再使用单参数就很困难了
- from functools import cmp_to_key
- lst = ['2', '4', '3']
- lst.sort(key=cmp_to_key(lambda x, y: int(x + y) - int(y + x)))
- print(lst) # ['2', '3', '4']
- # cmp_to_key(func)接受一个双参数函数,这个双参数函数返回的值必须可以和整型比较大小
- # 当func>0时则判断x>y,func==0时判断x==y,func<0时判断x
=也同理 - # 该函数的实现方法可以去看源码,以后有机会我也会继续写关于python的标准库内容
- # 提示:本质上与排序的方法有关,list内置方法sort是基于比较的排序
- # 我们只需要重新规定比较的法则即可,基于非比较的排序是不能这样改变排序方法的