Python列表从入门到精通:创建、增删改查、切片与实战应用

发布时间:2026/8/27 1:34:02
Python列表从入门到精通:创建、增删改查、切片与实战应用 1. 从“容器”到“瑞士军刀”为什么Python列表是第一个必须精通的工具如果你刚开始学Python或者已经写了几行print(“Hello World”)那么接下来你遇到的第一个真正意义上的“生产力工具”十有八九就是列表。很多人把它理解成一个简单的“数组”或者“装东西的盒子”这种理解没错但太浅了。在我十多年的编程和教学经验里列表更像是Python送给你的一把“瑞士军刀”——它看似简单但刀、剪、锉、锯功能俱全几乎贯穿了你未来所有代码的骨架。想想看你要处理一批用户名字、计算一组考试成绩、存储从文件里读取的每一行数据、甚至是临时记录程序运行中的各种状态用什么列表。它有序、可变、能装下任何类型的数据这种灵活性是其他语言里需要额外声明和复杂操作才能实现的。网络上热门的“人狗大作战python代码2023”或者各种游戏源码其核心的角色属性、技能列表、地图坐标底层几乎都是列表在支撑。而“数据结构期末复习”时列表的操作更是必考的基础。所以这第08课远不止是学一个语法而是为你打开用程序高效处理现实世界数据的大门。这节课我们不搞花架子就扎扎实实地把列表这把“瑞士军刀”的每一个基础功能部件拆开、讲透。从如何“打造”创建它到如何“使用”增删改查它再到一些“高级技巧”切片、常用方法我会结合大量即写即用的完整代码示例让你看完就能上手避免那些我早期踩过的“坑”。我们的目标很明确让你彻底理解列表并能用它解决实际的小问题比如计算平均分、过滤数据为后续学习更复杂的数据结构如字典、集合和算法打下坚实的基础。2. 列表的创建与基本访问你的第一个数据“收纳盒”在动手写代码之前我们先在脑子里建立对列表的直观印象。你可以把它想象成一个带编号的储物柜或者一列火车车厢。每个位置我们叫它“索引”都有一个唯一的编号从0开始计数。最关键的是每个“储物格”里放什么东西都行数字、字符串、甚至另一个列表没错柜子里可以再套小柜子。2.1 三种创建方式从简单到灵活创建列表就像准备一个空盒子或者预先装好物品的盒子。Python提供了非常直观的语法。方式一使用方括号[]直接创建这是最常用、最直接的方式。# 创建一个空列表就像一个还没拆封的空收纳盒 empty_list [] print(f“空列表{empty_list}”) # 输出空列表[] # 创建一个包含多个元素的列表初始化就把东西放好 fruits [“苹果”, “香蕉”, “橙子”, “葡萄”] numbers [1, 2, 3, 4, 5] mixed_bag [42, “Hello”, 3.14, True] # 列表可以混合存放不同类型的数据 print(f“水果列表{fruits}”) print(f“混合列表{mixed_bag}”)注意很多新手会忘记逗号写成[“苹果” “香蕉”]这是语法错误。列表元素之间必须用逗号分隔。方式二使用list()构造函数转换list()函数可以将其他可迭代的对象比如字符串、元组、甚至是range生成的范围转换成列表。这在数据处理中非常有用。# 将字符串的每个字符变成列表元素 word_list list(“Python”) print(word_list) # 输出[‘P’, ‘y’, ‘t’, ‘h’, ‘o’, ‘n’] # 将数字范围转换成列表 numbers_from_range list(range(5)) # range(5)生成0,1,2,3,4 print(numbers_from_range) # 输出[0, 1, 2, 3, 4] # 将元组一种不可变的序列转换成列表 a_tuple (1, 2, 3) list_from_tuple list(a_tuple) print(list_from_tuple) # 输出[1, 2, 3]这种方式特别适合当你需要修改一个原本不可变的数据序列如字符串或元组时。方式三使用列表推导式List Comprehension创建这是一种更高级、更Pythonic符合Python风格的创建方式它非常简洁和高效。虽然我们后面会详细讲但先看一个简单例子建立概念。# 创建一个包含0到9每个数字平方的列表 squares [x**2 for x in range(10)] print(squares) # 输出[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]这一行代码等价于一个好几行的for循环。它体现了Python“用一行代码做很多事”的哲学是提升代码效率和可读性的利器。2.2 访问元素通过“索引”精准定位列表创建好了我们怎么把里面的东西拿出来或者看看呢这就用到了“索引”。记住两个关键点索引从0开始可以用负数从末尾倒着数。fruits [“苹果”, “香蕉”, “橙子”, “葡萄”, “芒果”] # 正向索引从0开始 first_fruit fruits[0] # “苹果” second_fruit fruits[1] # “香蕉” print(f“第一个水果是{first_fruit}”) print(f“第三个水果是{fruits[2]}”) # 直接嵌入打印“橙子” # 负向索引从-1开始 last_fruit fruits[-1] # “芒果” second_last_fruit fruits[-2] # “葡萄” print(f“最后一个水果是{last_fruit}”) print(f“倒数第二个水果是{second_last_fruit}”)这里有一个我早期常犯的错误也是很多新手会遇到的“坑”索引越界。# 错误示例 fruits [“苹果”, “香蕉”, “橙子”] # print(fruits[3]) # 这行会报错IndexError: list index out of range # print(fruits[-4]) # 同样会报错因为只有3个元素负索引-4也越界了列表只有3个元素有效索引是0, 1, 2 和 -1, -2, -3。试图访问fruits[3]就像去一个只有3个柜子的储物柜开第4个门当然会出错。在写代码时尤其是在循环中动态计算索引时一定要时刻警惕边界情况。2.3 修改元素列表是“可变”的这是列表与字符串、元组等不可变数据类型的一个核心区别。你可以直接通过索引给某个位置“重新赋值”。fruits [“苹果”, “香蕉”, “橙子”] print(f“修改前{fruits}”) # [‘苹果’ ‘香蕉’ ‘橙子’] fruits[1] “蓝莓” # 把索引1第二个元素从“香蕉”改成“蓝莓” print(f“修改后{fruits}”) # [‘苹果’ ‘蓝莓’ ‘橙子’] # 甚至可以修改成不同类型的数据 fruits[2] 100 print(f“再次修改后{fruits}”) # [‘苹果’ ‘蓝莓’ 100]这种“可变性”赋予了列表极大的灵活性但也带来了需要小心维护数据一致性的责任。3. 列表的核心操作增、删、查、改的完整工具箱仅仅会创建和访问还不够我们得能动态地管理这个“收纳盒”里的内容。下面这些方法是你必须像用筷子一样熟练的核心操作。3.1 增加元素四种方法应对不同场景向列表里添加新元素根据你想加在什么位置有四种主要方法。append(item)在列表末尾添加一个元素这是最常用的方法就像往队伍最后面排一个人。todo_list [“写博客” “买菜”] todo_list.append(“学Python列表”) print(todo_list) # 输出[‘写博客’ ‘买菜’ ‘学Python列表’] # 可以连续append todo_list.append(“跑步”) todo_list.append(“看书”) print(todo_list) # 输出[‘写博客’ ‘买菜’ ‘学Python列表’ ‘跑步’ ‘看书’]append()每次只添加一个元素。如果你想添加另一个列表的所有元素需要用extend()或者运算符。insert(index, item)在指定索引位置插入元素这个方法让你可以“插队”。它需要两个参数要插入的位置索引和要插入的元素。fruits [“苹果” “橙子” “葡萄”] fruits.insert(1, “香蕉”) # 在索引1第二个位置插入“香蕉” print(fruits) # 输出[‘苹果’ ‘香蕉’ ‘橙子’ ‘葡萄’] # 原来的“橙子”和“葡萄”自动向后移动了一位 # 在开头插入 fruits.insert(0, “火龙果”) print(fruits) # 输出[‘火龙果’ ‘苹果’ ‘香蕉’ ‘橙子’ ‘葡萄’] # 在末尾插入等价于append但更推荐直接用append fruits.insert(len(fruits), “芒果”) # len(fruits)获取列表长度即最后一个位置之后 print(fruits) # 输出[‘火龙果’ ‘苹果’ ‘香蕉’ ‘橙子’ ‘葡萄’ ‘芒果’]注意insert操作在列表开头或中间插入元素时需要移动其后的所有元素对于很长的列表这是一个相对耗时的操作时间复杂度O(n)。如果频繁在开头插入可以考虑使用collections.deque双端队列。extend(iterable)将另一个可迭代对象的所有元素添加到列表末尾这是用来合并列表的利器避免了用循环一个个append的麻烦。list_a [1, 2, 3] list_b [4, 5, 6] list_a.extend(list_b) print(list_a) # 输出[1, 2, 3, 4, 5, 6] print(list_b) # 输出[4, 5, 6] (list_b本身没有被改变) # 也可以用 运算符但注意区别 list_c [1, 2, 3] list_d [4, 5, 6] new_list list_c list_d print(new_list) # 输出[1, 2, 3, 4, 5, 6] print(list_c) # 输出[1, 2, 3] (list_c没有被改变) print(list_d) # 输出[4, 5, 6] (list_d没有被改变)关键区别extend()是修改原列表list_a而运算符是创建一个全新的列表new_list原列表不变。根据你是否需要保留原列表来选择使用哪种方式。使用运算符连接列表如上例所示可以连接两个列表生成一个新列表。它非常直观但需要注意性能因为每次连接都会创建新列表并复制所有元素。对于大量数据的合并extend()通常是更好的选择。3.2 删除元素四种方法精准移除有增就有删删除元素同样有多种方式适用于不同场景。remove(item)删除第一个匹配到的指定值的元素当你不知道元素的索引但知道它的值时用这个方法。pets [“猫” “狗” “兔子” “狗” “仓鼠”] pets.remove(“狗”) print(pets) # 输出[‘猫’ ‘兔子’ ‘狗’ ‘仓鼠’] # 只删除了第一个出现的“狗”踩坑提示如果要删除的值在列表中不存在Python会抛出ValueError。所以在使用前最好先用if item in list:判断一下。if “龙” in pets: pets.remove(“龙”) else: print(“列表里没有‘龙’这个宠物”)pop(index)删除并返回指定索引位置的元素这是一个非常有用且独特的方法。它不仅能删除元素还能把这个被删除的元素“拿回来”用。如果不指定索引默认删除并返回最后一个元素。stack [“任务A” “任务B” “任务C”] completed_task stack.pop() # 默认删除最后一个模拟“后进先出”的栈 print(f“完成的任务是{completed_task}”) # 输出完成的任务是任务C print(f“剩余任务{stack}”) # 输出剩余任务[‘任务A’ ‘任务B’] # 删除指定索引的元素 second_task stack.pop(0) # 删除索引0的元素模拟“先进先出”的队列 print(f“接下来做{second_task}”) # 输出接下来做任务A print(f“最后剩余{stack}”) # 输出最后剩余[‘任务B’]pop()是实现栈LIFO和队列FIFO行为的基础。同样要注意索引越界问题。del语句根据索引或切片删除元素del是Python的一个语句不是列表的方法。它更底层可以直接删除列表中的元素、切片甚至删除整个列表变量。numbers [10, 20, 30, 40, 50] del numbers[2] # 删除索引为2的元素30 print(numbers) # 输出[10, 20, 40, 50] del numbers[1:3] # 删除切片索引1到2不包含3的元素 print(numbers) # 输出[10, 50] # 删除整个列表引用 del numbers # print(numbers) # 这行会报错NameError: name ‘numbers’ is not defineddel非常强大但通常pop()和remove()在语义上更清晰。del常用于你需要“彻底移除”某块内存引用时。clear()清空列表所有元素这个方法快速将列表重置为空列表但列表对象本身仍然存在。temp_data [1, 2, 3, 4, 5] temp_data.clear() print(temp_data) # 输出[] # 列表还在只是空了3.3 查找与统计快速定位和汇总信息index(item, start, end)返回元素第一次出现的索引这个方法帮你找到某个值在列表中的“座位号”。colors [“红” “蓝” “绿” “黄” “蓝” “红”] idx colors.index(“蓝”) print(f“第一次出现‘蓝’的索引是{idx}”) # 输出1 # 可以指定搜索的起始和结束位置 idx2 colors.index(“蓝” 2) # 从索引2开始找 print(f“从索引2开始第一次出现‘蓝’的索引是{idx2}”) # 输出4 # 如果找不到会抛出ValueError # idx3 colors.index(“紫”) # ValueError: ‘紫’ is not in list同样使用前用in关键字判断是否存在是更安全的做法。count(item)统计元素出现的次数快速数一数某个值在列表里出现了多少次。survey_results [“赞成” “反对” “赞成” “弃权” “赞成” “反对”] yes_count survey_results.count(“赞成”) no_count survey_results.count(“反对”) print(f“赞成票{yes_count} 反对票{no_count}”) # 输出赞成票3 反对票2in和not in成员运算符判断元素是否存在这是最常用的查找操作返回布尔值True或False。shopping_list [“牛奶” “面包” “鸡蛋”] if “面包” in shopping_list: print(“面包已经在购物清单里了。”) if “咖啡” not in shopping_list: print(“记得买咖啡”)in运算符的底层实现会遍历列表对于非常大的列表频繁查找效率可能不高这时可以考虑使用集合set来获得O(1)的查找效率。4. 列表的“高级”技巧切片、复制与排序掌握了增删改查你已经能解决80%的问题。下面这些技巧能让你的代码更简洁、更高效、更Pythonic。4.1 列表切片一次性获取子序列的“神技”切片是Python序列类型列表、字符串、元组最优雅的特性之一。它允许你通过指定一个索引范围来获取列表的一部分并返回一个新的列表。语法是list[start:stop:step]。start起始索引包含默认为0。stop结束索引不包含默认为列表长度。step步长默认为1。可以为负表示反向切片。numbers [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 基本切片获取索引2到5不包含5的元素 slice1 numbers[2:5] print(slice1) # 输出[2, 3, 4] # 从头开始切片 slice2 numbers[:4] # 等价于 numbers[0:4] print(slice2) # 输出[0, 1, 2, 3] # 切片到末尾 slice3 numbers[6:] # 从索引6到末尾 print(slice3) # 输出[6, 7, 8, 9] # 使用负索引切片 slice4 numbers[-3:] # 获取最后三个元素 print(slice4) # 输出[7, 8, 9] slice5 numbers[:-3] # 获取除了最后三个以外的所有元素 print(slice5) # 输出[0, 1, 2, 3, 4, 5, 6] # 使用步长 slice6 numbers[::2] # 获取所有偶数索引步长为2的元素 print(slice6) # 输出[0, 2, 4, 6, 8] slice7 numbers[1::2] # 获取所有奇数索引的元素 print(slice7) # 输出[1, 3, 5, 7, 9] # 反向切片步长为负 slice8 numbers[::-1] # 反转列表得到一个全新的反转列表 print(slice8) # 输出[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] slice9 numbers[5:2:-1] # 从索引5到索引2不包含2反向步进 print(slice9) # 输出[5, 4, 3]切片的核心要点切片操作不会修改原列表而是返回一个新的列表对象。这在你需要基于原列表创建不同视图时非常有用且安全。4.2 列表的复制浅拷贝与深拷贝的“陷阱”这是Python初学者甚至是有经验的开发者都容易混淆和出错的地方。直接赋值、切片复制、copy()方法它们之间有本质区别。情况一直接赋值不是复制是引用别名list_original [1, 2, [3, 4]] list_alias list_original # 这只是给同一个列表对象起了个新名字 list_alias.append(5) print(list_original) # 输出[1, 2, [3, 4], 5] print(list_alias) # 输出[1, 2, [3, 4], 5] # 两个变量指向同一个列表修改任何一个另一个也“看”到变化list_alias不是新列表它只是原列表的一个“标签”。这通常不是你想要的。情况二浅拷贝Shallow Copy浅拷贝会创建一个新的列表对象但新列表中的元素是对原列表元素的引用如果元素是可变对象如子列表。import copy list_original [1, 2, [3, 4]] list_shallow list_original.copy() # 或者用 list_original[:] 或 copy.copy(list_original) # 修改第一层元素不可变类型互不影响 list_shallow[0] 100 print(list_original) # 输出[1, 2, [3, 4]] print(list_shallow) # 输出[100, 2, [3, 4]] # 修改第二层可变元素子列表会相互影响 list_shallow[2].append(999) print(list_original) # 输出[1, 2, [3, 4, 999]] # 原列表也被改了 print(list_shallow) # 输出[100, 2, [3, 4, 999]]copy()和切片[:]都是浅拷贝。它们只复制了列表的“外壳”里面的“房间”可变元素还是共享的。这是很多隐蔽Bug的来源。情况三深拷贝Deep Copy深拷贝会递归地复制所有层级的对象创建一个完全独立的副本。import copy list_original [1, 2, [3, 4]] list_deep copy.deepcopy(list_original) list_deep[2].append(999) print(list_original) # 输出[1, 2, [3, 4]] # 原列表安然无恙 print(list_deep) # 输出[1, 2, [3, 4, 999]]当你需要完全独立地操作一个嵌套结构复杂的数据时务必使用copy.deepcopy()。4.3 列表排序让数据井然有序Python提供了两种主要的排序方式永久性排序修改原列表和临时性排序返回新列表。sort()方法原地排序修改原列表numbers [3, 1, 4, 1, 5, 9, 2] numbers.sort() # 默认升序 print(numbers) # 输出[1, 1, 2, 3, 4, 5, 9] # 降序排序 numbers.sort(reverseTrue) print(numbers) # 输出[9, 5, 4, 3, 2, 1, 1] # 按字符串长度排序使用key参数 fruits [“apple”, “kiwi”, “banana”, “cherry”] fruits.sort(keylen) # key参数指定一个函数根据函数返回值排序 print(fruits) # 输出[‘kiwi’ ‘apple’ ‘cherry’ ‘banana’] (按长度4,5,6,6排序)sort()方法没有返回值或者说返回None它直接修改了原列表。sorted()函数返回新列表不修改原列表numbers [3, 1, 4, 1, 5, 9, 2] sorted_numbers sorted(numbers) # 返回一个新的排序后的列表 print(sorted_numbers) # 输出[1, 1, 2, 3, 4, 5, 9] print(numbers) # 输出[3, 1, 4, 1, 5, 9, 2] (原列表未变) # 同样支持reverse和key参数 sorted_fruits sorted(fruits, keylen, reverseTrue) print(sorted_fruits) # 输出[‘banana’ ‘cherry’ ‘apple’ ‘kiwi’]sorted()函数可以用于任何可迭代对象如字符串、元组并总是返回一个新的列表。reverse()方法原地反转列表顺序nums [1, 2, 3, 4, 5] nums.reverse() print(nums) # 输出[5, 4, 3, 2, 1]注意reverse()只是反转顺序不是排序。如果想按降序排序应该用sort(reverseTrue)。5. 综合实战用列表解决几个经典小问题理论讲得再多不如动手写一遍。下面我们用几个完整的小项目把今天学的所有知识点串起来。5.1 实战一学生成绩管理系统基础版我们来模拟一个简单的场景录入学生成绩计算平均分找出最高分和最低分并筛选及格的学生。# 初始化一个空列表来存储成绩 scores [] # 1. 录入成绩模拟输入这里用列表直接赋值代替 # 假设我们手动输入了5个成绩85, 92, 78, 60, 45 scores [85, 92, 78, 60, 45] print(f“所有学生成绩{scores}”) # 2. 计算平均分 average_score sum(scores) / len(scores) # sum()求和 len()求长度 print(f“平均分是{average_score:.2f}”) # :.2f 格式化保留两位小数 # 3. 找出最高分和最低分 max_score max(scores) min_score min(scores) print(f“最高分{max_score} 最低分{min_score}”) # 4. 筛选及格的学生假设60分及格 # 方法一使用循环和条件判断 passing_scores [] for score in scores: if score 60: passing_scores.append(score) print(f“方法一及格成绩{passing_scores}”) # 方法二使用列表推导式更简洁后面会详细讲 passing_scores_v2 [score for score in scores if score 60] print(f“方法二及格成绩{passing_scores_v2}”) # 5. 对成绩进行排序从高到低 sorted_scores_desc sorted(scores, reverseTrue) print(f“成绩排名从高到低{sorted_scores_desc}”) # 6. 查找某个学生的成绩假设我们知道成绩找索引 search_score 78 if search_score in scores: index scores.index(search_score) print(f“成绩 {search_score} 在列表中的索引是{index} (假设索引即学号)”) else: print(f“没有找到成绩为 {search_score} 的学生”)这个例子几乎用到了创建、访问、统计len,sum,max,min、查找in,index、增加append、排序sorted等所有基础操作。5.2 实战二待办事项管理器动态操作我们来模拟一个更动态的场景任务会不断添加、完成删除、以及重新排序。# 初始化待办事项 todo_list [“回复邮件” “写项目报告” “团队会议” “修复Bug”] print(“初始待办事项”, todo_list) # 1. 添加新任务来自不同渠道 todo_list.append(“代码审查”) # 常规追加 print(“追加新任务后”, todo_list) todo_list.insert(1, “紧急客户来电”) # 紧急任务插队 print(“插入紧急任务后”, todo_list) new_tasks [“更新文档” “性能测试”] todo_list.extend(new_tasks) # 批量添加 print(“批量添加后”, todo_list) # 2. 完成任务删除 # 假设“团队会议”开完了 if “团队会议” in todo_list: todo_list.remove(“团队会议”) print(“完成‘团队会议’后”, todo_list) # 假设我们按顺序处理完成第一个任务用pop模拟 if todo_list: # 确保列表不为空 completed todo_list.pop(0) print(f“已完成任务{completed}”) print(“当前待办”, todo_list) # 3. 任务排序按优先级或字母顺序 # 按字母顺序排序修改原列表 todo_list.sort() print(“按字母排序后”, todo_list) # 4. 查看特定范围的任务切片 print(“前3项任务”, todo_list[:3]) print(“最后2项任务”, todo_list[-2:]) # 5. 清空所有任务假设一天结束 # todo_list.clear() # print(“清空后”, todo_list)这个例子展示了列表如何作为一个动态的、可修改的容器来管理不断变化的数据。5.3 实战三理解浅拷贝陷阱调试案例让我们亲手制造并修复一个因浅拷贝引起的Bug印象会更深刻。print(“ 浅拷贝陷阱演示 ”) # 场景我们有一个模板配置想基于它创建几个不同的环境配置 template_config [“server_ip”, “192.168.1.1”, [“plugin_a”, “plugin_b”]] print(f“模板配置{template_config}”) # 错误做法直接用浅拷贝创建开发环境和测试环境配置 dev_config template_config.copy() # 浅拷贝 test_config template_config.copy() # 浅拷贝 dev_config[0] “dev_server_ip” dev_config[1] “10.0.0.1” # 我们只想为开发环境添加一个插件 dev_config[2].append(“plugin_dev_only”) print(f“修改后开发配置{dev_config}”) print(f“修改后测试配置{test_config}”) # 糟了测试配置的插件列表也被改了 print(f“原始模板{template_config}”) # 模板也被污染了 print(“\n 使用深拷贝修复 ) import copy template_config_fixed [“server_ip”, “192.168.1.1”, [“plugin_a”, “plugin_b”]] dev_config_fixed copy.deepcopy(template_config_fixed) test_config_fixed copy.deepcopy(template_config_fixed) dev_config_fixed[0] “dev_server_ip” dev_config_fixed[1] “10.0.0.1” dev_config_fixed[2].append(“plugin_dev_only”) print(f“修改后开发配置深拷贝{dev_config_fixed}”) print(f“修改后测试配置深拷贝{test_config_fixed}”) # 测试配置保持纯净 print(f“原始模板深拷贝{template_config_fixed}”) # 模板也保持纯净通过这个对比你能清晰地看到浅拷贝在处理嵌套可变对象时的风险以及深拷贝如何解决这个问题。在实际项目中尤其是配置管理、数据模板化等场景这个问题至关重要。列表的学习是Python编程中一个扎实的起点。它看似简单但蕴含了Python设计哲学中“简单而强大”的思想。我建议你不仅仅是阅读而是打开你的代码编辑器无论是VSCode、PyCharm还是简单的IDLE把上面的每一个例子都亲手敲一遍并尝试修改它们看看会发生什么。遇到错误不要怕那正是你理解加深的过程。当你能够不假思索地运用列表的切片、推导式和常用方法时你会发现处理数据变得如此自然流畅。在接下来的课程中我们将探索列表的“兄弟”——元组和字典并深入学习强大的列表推导式那时你对数据结构的掌控力会再上一个台阶。