一文吃透 Python 列表:从定义到拷贝,7 大操作一网打尽
1. 列表的定义在 Python 中列表list是一种有序、可变的容器类型用于存储多个元素。所谓有序是指列表中每个元素都有固定的位置索引插入顺序会被保留可变则意味着创建后可以随时增删改其中的元素这与元组tuple、字符串str等不可变类型有明显区别。定义列表有以下两种常用方式使用方括号[]最直接的方式在方括号内用逗号分隔各个元素即可创建包含初始元素的列表。写成[]则表示空列表。使用list()构造函数将可迭代对象如字符串、元组、range 对象、集合等转换为列表这对于动态生成列表或从其他数据结构转换非常实用。这里需要解释一下可迭代对象iterable的概念凡是能被for循环遍历的对象都属于可迭代对象。常见的有字符串遍历每个字符、range 对象遍历整数序列、元组、集合、字典的键等。将可迭代对象传入list()后会把其中的每个元素依次提取出来形成一个新的列表。# 方式一使用方括号定义 empty [] # 空列表 nums [10, 20, 30] # 包含三个整数的列表 print(empty, type(empty)) # [] class list print(nums, type(nums)) # [10, 20, 30] class list 方式二使用 list() 构造函数 chars list(hello) # 字符串 → 列表每个字符成为一个元素 print(chars) # [h, e, l, l, o] numbers list(range(5)) # range 对象 → 列表 print(numbers) # [0, 1, 2, 3, 4] 空列表的两种写法 a [] b list() print(a, b) # [] []2. 列表中可以放入任意内容Python 列表的最大特点之一就是类型不受限制。同一个列表中可以同时存放整数、浮点数、字符串、布尔值甚至还能嵌套其他列表、字典或自定义对象。这种灵活性让列表非常适合用来组织异构数据比如一条记录中同时包含姓名、年龄、成绩列表等字段。嵌套结构在实际开发中很常见例如用列表存储多个学生的成绩单每个学生又是一个列表或者用列表存储从 JSON 接口解析出的字典对象。不过也正因为这种灵活性在处理嵌套列表时需要格外注意修改嵌套的可变对象如子列表会同时影响引用它的地方这一点在后面浅拷贝一节会详细说明。# 混合存放不同类型的数据 info [张三, 25, 89.5, True, [数学, 英语]] print(info) # [张三, 25, 89.5, True, [数学, 英语]] 嵌套字典示例 students [ {name: 小明, score: 92}, {name: 小红, score: 88} ] print(students[0][name]) # 小明 甚至可以存放 None data [1, None, abc] print(data) # [1, None, abc]3. 索引与遍历列表中的每个元素都有一个位置编号称为索引index。Python 的索引从0开始计数也就是说第一个元素的索引是 0第二个是 1以此类推。最后一个元素的索引是len(列表) - 1。除了正向索引Python 还支持负数索引即从列表末尾反向访问-1表示最后一个元素-2表示倒数第二个元素以此类推。这在需要快速获取尾部元素时非常方便不需要先计算列表长度。使用内置函数len()可以获取列表的元素个数即长度。fruits [苹果, 香蕉, 橙子, 葡萄, 西瓜] 正向索引 print(fruits[0]) # 苹果 —— 第一个 print(fruits[2]) # 橙子 —— 第三个 负数索引 print(fruits[-1]) # 西瓜 —— 最后一个 print(fruits[-3]) # 橙子 —— 倒数第三个 len() 获取长度 print(len(fruits)) # 5遍历列表是对每个元素逐一执行相同操作的过程Python 提供了两种主流方式直接遍历元素——使用for 变量 in 列表的语法循环变量会依次获取列表中的每个元素值。这种方式简洁直观适用于只需要读取元素内容、不需要知道它排在第几个的场景。通过索引遍历——借助range(len(列表))生成一个从 0 到 len-1 的整数序列然后在循环体内用列表[i]访问每个元素。这种方式适合需要同时知道位置和内容的场景比如修改某个位置的元素或者需要比较相邻元素。scores [85, 92, 78, 95, 88] 方式一直接遍历元素 —— 最常用 for s in scores: print(s) 方式二通过索引遍历 —— 需要位置信息时使用 for i in range(len(scores)): print(f第{i}个学生成绩{scores[i]})索引遍历实战判断列表是否对称回文所谓对称是指列表从前往后读和从后往前读完全一样例如[1, 2, 3, 2, 1]。利用首尾配对比较的思路将第i个元素与倒数第i个元素逐一比较只要有一对不相等就可以判定为不对称并提前退出。由于每轮比较首尾两个元素只需循环到列表长度的一半即可。nums [1, 2, 3, 2, 1] length len(nums) is_symmetric True for i in range(length // 2): # nums[i] 与 nums[length - 1 - i] 是对称位置 if nums[i] ! nums[length - 1 - i]: is_symmetric False print(f不对称{nums[i]} ! {nums[length - 1 - i]}) break if is_symmetric: print(列表对称) 更简洁的方法直接与反转后的列表比较 print(nums nums[::-1]) # True4. 列表的常见操作Python 为列表提供了丰富的内置方法涵盖查找、统计、插入、删除、排序、反转和拷贝等日常开发中的高频需求。下面逐一介绍这些方法的用法和注意事项。4.1 index —— 查找元素位置index(value)方法用于查找指定值在列表中第一次出现的位置返回其索引。如果列表中不存在该值则会抛出 ValueError 异常导致程序中断。因此在实际使用中建议先使用in运算符判断元素是否存在再调用index()这是一种稳健的编码习惯。如果列表中包含多个相同的值index()只返回第一个匹配项的索引后面的同名元素需要额外处理比如切片后再查找。colors [红, 蓝, 绿, 蓝, 黄] 查找第一个蓝的位置 print(colors.index(蓝)) # 输出 1 如果要查找的值不存在会抛出异常 colors.index(紫) # ValueError: 紫 is not in list 安全做法先用 in 判断 target 紫 if target in colors: print(colors.index(target)) else: print(f{target} 不在列表中)4.2 count —— 统计元素出现次数count(value)方法统计某个值在列表中出现的总次数。如果该元素不存在则返回0不会抛出异常。这个方法常用于统计词频、判断重复数据等场景。votes [赞成, 反对, 赞成, 赞成, 弃权] print(votes.count(赞成)) # 3 print(votes.count(反对)) # 1 print(votes.count(无效)) # 0 —— 不存在返回 0不会报错4.3 插入操作列表提供了三种向其中添加元素的方法分别适用于不同的插入需求方法说明示例append(obj)将单个元素追加到列表末尾这是最常用的添加方式items.append(4)insert(index, obj)在指定索引位置插入元素原位置及之后的元素依次后移items.insert(0, 开头)extend(iterable)将可迭代对象中的每个元素逐个追加到末尾相当于批量添加items.extend([5, 6, 7])需要注意的是append()是把参数作为一个整体追加即使传入了列表也不会展开而extend()会遍历参数中的每个元素逐个添加到末尾。这两者的区别在初学者中常被混淆。items [1, 2, 3] append —— 追加单个元素 items.append(4) print(items) # [1, 2, 3, 4] insert —— 在指定位置插入 items.insert(0, start) # 在索引 0 处插入 print(items) # [start, 1, 2, 3, 4] items.insert(2, mid) # 在索引 2 处插入 print(items) # [start, 1, mid, 2, 3, 4] extend —— 批量追加 items.extend([5, 6, 7]) print(items) # [start, 1, mid, 2, 3, 4, 5, 6, 7] append 和 extend 的区别 a [1, 2] a.append([3, 4]) # 把 [3, 4] 作为单个元素追加 print(a) # [1, 2, [3, 4]] b [1, 2] b.extend([3, 4]) # 把 [3, 4] 中的元素逐个追加 print(b) # [1, 2, 3, 4]4.4 删除操作列表支持多种删除元素的方式可以按位置、值或者一次性清空来移除元素。下表汇总了四种删除方法方法说明返回值pop()删除并返回末尾元素类似弹出栈顶被删除的元素pop(index)删除并返回指定索引的元素索引越界会报错被删除的元素remove(value)删除第一个匹配的指定值不存在则抛出 ValueError无返回 Noneclear()清空整个列表移除所有元素无返回 Nonepop()的有用之处在于它既能删除元素又能返回被删除的值这在实现栈后进先出或队列等数据结构时非常方便。remove()只删除第一个匹配项如果列表中有多个相同的值需要全部删除则需要配合循环使用。tasks [任务A, 任务B, 任务C, 任务D, 任务E] pop() —— 删除并返回末尾元素 last tasks.pop() print(last) # 任务E print(tasks) # [任务A, 任务B, 任务C, 任务D] pop(index) —— 删除并返回指定位置元素 first tasks.pop(0) print(first) # 任务A print(tasks) # [任务B, 任务C, 任务D] remove() —— 按值删除第一个匹配项 tasks.append(任务B) # 先添加一个重复元素 print(tasks) # [任务B, 任务C, 任务D, 任务B] tasks.remove(任务B) # 删除第一个任务B print(tasks) # [任务C, 任务D, 任务B] 安全做法先判断值是否存在再删除 if 任务X in tasks: tasks.remove(任务X) clear() —— 清空整个列表 tasks.clear() print(tasks) # []4.5 排序 —— sortsort()方法对列表进行原地排序in-place即直接修改原列表的元素顺序不创建新列表。默认按升序排列从小到大设置reverseTrue则按降序排列从大到小。需要注意sort()只能用于元素类型相同的列表如全是数字或全是字符串否则 Python 无法比较不同类型的大小会抛出 TypeError。如果希望保留原列表不变、只返回排序后的新列表可以使用内置函数sorted()它会生成一个新的排序列表而不会修改原列表。scores [72, 95, 88, 60, 91, 85] 升序排序 scores.sort() print(scores) # [60, 72, 85, 88, 91, 95] 降序排序 scores.sort(reverseTrue) print(scores) # [95, 91, 88, 85, 72, 60] sorted() 返回新列表原列表不变 original [3, 1, 4, 1, 5] result sorted(original) print(result) # [1, 1, 3, 4, 5] print(original) # [3, 1, 4, 1, 5] —— 原列表未变4.6 逆序 —— reversereverse()方法将列表中的元素首尾反转即第一个变成最后一个、第二个变成倒数第二个以此类推。它与排序不同不涉及元素之间的大小比较只是纯粹地颠倒顺序。这在需要回放历史记录、逆向遍历等场景中很实用。如果想要顺序反转但不修改原列表可以使用切片列表[::-1]生成一个反转后的新列表。letters [a, b, c, d, e] 原地反转 letters.reverse() print(letters) # [e, d, c, b, a] 再次反转则恢复原顺序 letters.reverse() print(letters) # [a, b, c, d, e] 使用切片生成反转后的新列表不修改原列表 reversed_letters letters[::-1] print(reversed_letters) # [e, d, c, b, a] print(letters) # [a, b, c, d, e] —— 原列表不变4.7 浅拷贝 —— copy补充copy()方法返回列表的浅拷贝shallow copy。浅拷贝意味着新列表会复制原列表中每个元素的引用而不是递归地复制元素本身。具体影响如下对于不可变元素如整数、字符串、元组等修改新列表中的这类元素不会影响原列表因为不可变元素被替换时会指向新对象。对于可变元素如嵌套的子列表、字典等新列表和原列表共享同一个内部对象。修改新列表中嵌套对象的内部内容原列表中的对应嵌套对象也会同步变化。如果需要完全独立的副本连嵌套对象也复制应使用copy模块中的deepcopy()方法进行深拷贝。import copy original [1, 2, [10, 20]] 浅拷贝 shallow original.copy() 修改不可变元素 —— 不影响原列表 shallow[0] 999 print(original) # [1, 2, [10, 20]] —— 不变 修改嵌套列表中的元素 —— 会影响原列表 shallow[2][0] 888 print(original) # [1, 2, [888, 20]] —— 原列表被联动修改 深拷贝 —— 完全独立的副本 original2 [1, 2, [10, 20]] deep copy.deepcopy(original2) deep[2][0] 999 print(original2) # [1, 2, [10, 20]] —— 原列表不受影响 print(deep) # [1, 2, [999, 20]]