Python列表是使用频率最高的容器类型之一,它有序、可变、可重复,并且可以容纳任意类型元素。列表保存的是对象引用,不是对象本身,因此 a==b 比较内容,a is b 判断是否同一个对象。嵌套可变对象被修改时,外层列表会同步反映,这是排查列表复制类 bug 的关键。
- a=[1,2,3]; b=[1,2,3]; print(a==b, a is b) # True False
- x='hello'; z=[1,2]; lst=[x,z]
- x='world'; z.append(3)
- print(lst) # ['hello', [1, 2, 3]]
复制代码
创建列表可用字面量、list()、列表推导式、重复乘法、split()、range(),也可从文件、生成器、JSON 等数据源转换。嵌套列表使用乘法要特别小心:[[0]*3]*3 会让三行指向同一个内部列表;正确方式是 [[0]*3 for _ in range(3)]。
- matrix_bug=[[0]*3]*3; matrix_bug[0][0]=1
- print(matrix_bug) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
- matrix_good=[[0]*3 for _ in range(3)]; matrix_good[0][0]=1
- print(matrix_good) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
- print(list('hello')) # ['h', 'e', 'l', 'l', 'o']
- print(list(range(5))) # [0, 1, 2, 3, 4]
- print('a,b,c'.split(',')) # ['a', 'b', 'c']
复制代码
索引从 0 开始,负索引 -1 表示最后一个元素。索引越界会抛 IndexError,切片不会越界,会自动截断。遍历列表时,直接遍历元素最简洁;需要序号时可用 enumerate(),同时遍历多个列表可用 zip(),反向遍历用 reversed(),排序后遍历用 sorted()。
- fruits=['苹果','香蕉','橘子']
- print(fruits[0], fruits[-1]) # 苹果 橘子
- nums=[0,1,2,3,4,5]
- print(nums[2:5], nums[::2], nums[::-1])
- for i,fruit in enumerate(fruits, start=1):
- print(i, fruit)
- for name,score in zip(['小明','小红'],[85,92]):
- print(name, score)
复制代码
增删改查方面,修改单个元素可直接下标赋值;切片赋值既能批量替换,也能改变列表长度。append() 在末尾追加一个对象,extend() 把另一个可迭代对象的元素逐个追加,insert() 在指定位置插入。pop() 返回并删除元素,del 按位置删除但不返回,remove() 按值删除第一个匹配项,不存在会抛 ValueError,clear() 清空列表。index() 查找第一次出现的位置,可指定范围,不存在会抛 ValueError;count() 统计出现次数,不存在返回 0。
- nums=[1,2,3]; nums[1]=20; nums[1:3]=[100]
- print(nums) # [1, 100]
- lst=[1,2]; lst.append([3,4])
- print(lst) # [1, 2, [3, 4]]
- lst2=[1,2]; lst2.extend([3,4])
- print(lst2) # [1, 2, 3, 4]
- fruits=['苹果','香蕉','橘子','香蕉']
- print(fruits.pop()) # 香蕉
- print(fruits.index('香蕉')) # 1
- nums=[1,3,5,3]; nums.remove(3)
- print(nums) # [1, 5, 3]
复制代码
复制列表时,赋值不是复制。b=a 只是让两个变量指向同一个列表,修改 b 会影响 a。浅拷贝可用 original[:]、list(original)、original.copy()、copy.copy()。浅拷贝只复制第一层,如果列表里嵌套了可变对象,内层仍然共享。需要完全独立时使用 copy.deepcopy(),它会递归复制所有层级,但性能代价更高。只有不可变元素时,浅拷贝通常就够。
- a=[1,2,3]; b=a; b[0]=100
- print(a) # [100, 2, 3]
- original=[[1,2],[3,4]]; shallow=original.copy()
- original[0][0]=999
- print(shallow) # [[999, 2], [3, 4]]
- import copy
- original=[[1,2],[3,4]]; deep=copy.deepcopy(original)
- original[0][0]=999
- print(deep) # [[1, 2], [3, 4]]
复制代码
列表推导式中,条件过滤写在 for 后面;if-else 三元表达式写在 for 前面,用于转换每个元素;多个 if 可连续写,表示同时满足条件。嵌套循环推导式可生成笛卡尔积或提取字段。
- evens=[x for x in range(10) if x % 2 == 0]
- labels=['偶数' if x%2==0 else '奇数' for x in range(5)]
- result=[x for x in range(50) if x%3==0 if x%5==0]
- pairs=[(x,y) for x in range(2) for y in range(2)]
- print(evens, labels, result, pairs)
复制代码
性能优化方面,原文提到列表扩容机制、预分配与逐步 append 的对比,以及避免不必要的列表复制。实际写代码时可据此取舍:已知数据规模时优先用列表推导式或预分配,减少逐步扩容;只在需要完全独立副本时才用 deepcopy;浅拷贝能解决时不要深拷贝。列表存引用的特性也会影响嵌套复制、批量收集和函数传参场景。
- result=[]
- for i in range(10):
- if i % 3 == 0 or i % 5 == 0:
- result.append(i)
- print(result) # [0, 3, 5, 6, 9]
- squares=[x ** 2 for x in range(10)]
- print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
复制代码
小结:列表的核心不只在 API 多,而在于理解它是有序可变的引用容器。创建时注意嵌套乘法的引用陷阱,增删改查时区分按位置和按值操作,复制时区分赋值、浅拷贝和深拷贝,推导式注意 if 与 if-else 的位置差异。把这些基础打牢,再进入切片、排序和更复杂的数据处理会顺畅很多。 |