目录开篇什么是列表1.创建列表1.直接使用方括号2.使用 list() 构造器3.列表推导式 —— 一行代码批量生产4.用乘法 * 快速初始化重要警告2.查找列表1.索引从 0 开始的位置编号2.负索引从尾巴开始数3. 切片取一段子列表4.查找元素的索引index()5. 统计出现次数count()6. 判断是否存在in 与 not in7.获取列表长度len()3.修改列表1. 通过索引修改元素2. 在末尾追加append()3. 在中间插入insert()4. 扩展列表extend() 与 5. 修改切片一套强大的批量替换6. 其他修改方式排序与反转4.删除列表元素1. pop(索引) —— 删除并返回指定位置的元素2.remove(值) —— 删除第一个匹配的值3.clear() —— 清空整个列表4. del 语句 —— 删除切片或整个变量5.进阶扩展1.底层实现使用层面的区别2.复制列表浅拷贝 vs 深拷贝3. 列表作为栈和队列4.用 enumerate() 同时获取索引和值5. 列表推导式的高级用法6. 将列表当作“参数包” —— * 解包7. 常见陷阱与避坑指南开篇什么是列表想象你有一个购物清单苹果、牛奶、面包、鸡蛋。你需要一个地方按顺序记下这些东西随时可以添加新物品、划掉已买的、查看是否还有某样东西。在 Python 中列表list就是这样的“超级购物清单”。列表是一种有序、可变、可包含任意类型元素的容器。用方括号[]表示元素之间用逗号分隔。shopping_list [苹果, 牛奶, 面包, 鸡蛋]可以在列表中放数字、字符串、甚至另一个列表……几乎任何东西。1.创建列表1.直接使用方括号最常见的方式直接将元素写在[]中fruits [苹果, 香蕉, 橙子] # 字符串列表 numbers [1, 2, 3, 4, 5] # 整数列表 mixed [42, hello, 3.14, True] # 混合类型列表完全合法 empty [] # 空列表列表的元素可以是任何类型数字、字符串、小数、布尔值甚至另一个列表。同一个列表里可以混装不同类型比如[42, hello, 3.14, True]。小贴士列表的名字通常用复数形式如fruits、numbers暗示它包含多个值。2.使用list()构造器list()就像一个“转换器”可以把其他序列变成列表。可以从其他可迭代对象如字符串、元组、range创建列表# 把字符串拆成一个个字符 chars list(abc) print(chars) # [a, b, c] # 把 range 对象变成列表 numbers list(range(5)) print(numbers) # [0, 1, 2, 3, 4] # 从元组另一种不可变的序列创建 tup (1, 2, 3) lst list(tup) print(lst) # [1, 2, 3] # 创建空列表的另一种方式 empty list() print(empty) # []Python 中的元组tuple是一种不可变的序列可以存储任意类型的元素混合类型用圆括号表示3.列表推导式 —— 一行代码批量生产假设你想生成 0 到 9 的平方数列表。传统方式要写三四行循环而列表推导式只需要一行squares [x**2 for x in range(10)] print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]我们来拆解这行代码for x in range(10)循环变量x依次取 0~9。x**2对每个x计算平方。 **幂运算符[...]把这些结果收集起来组成列表。还可以加上条件过滤用之前的一些知识比如只保留偶数的平方even_squares [x**2 for x in range(10) if x % 2 0] print(even_squares) # [0, 4, 16, 36, 64]4.用乘法*快速初始化如果需要一个重复元素的列表可以用“乘号”zeros [0] * 5 print(zeros) # [0, 0, 0, 0, 0] stars [*] * 3 print(stars) # [*, *, *]重要警告如果列表里装的是可变对象比如另一个列表乘法会复制引用导致意想不到的后果。matrix [[0] * 3] * 2 # 看起来像是两行三列的矩阵 print(matrix) # [[0, 0, 0], [0, 0, 0]] matrix[0][0] 1 print(matrix) # [[1, 0, 0], [1, 0, 0]] —— 第一列的两个元素都变了这是因为[0]*3先创建了一个单行列表然后*2复制了它的引用导致两行指向同一块内存。正确创建二维列表应该用列表推导式matrix [[0] * 3 for _ in range(2)] # 生成两个独立的小列表2.查找列表创建好列表后你需要知道怎么查看里面的内容。列表是有序的每个元素都有一个位置编号我们称之为索引。1.索引从 0 开始的位置编号生活中我们数数一般从 1 开始但 Python以及绝大多数编程语言从0开始。所以第一个元素的索引是 0第二个是 1第三个是 2……其实跟C/C下标一样fruits [苹果, 香蕉, 橙子, 葡萄]# 索引: 0 1 2 3要取某个位置的元素用 列表名[索引]:print(fruits[0]) # 苹果 print(fruits[2]) # 橙子如果你尝试访问一个不存在的索引比如fruits[10]Python 会报错IndexError: list index out of range。2.负索引从尾巴开始数Python 有个非常贴心的设计你可以用负整数从末尾开始数。-1代表最后一个-2代表倒数第二个依此类推。print(fruits[-1]) # 葡萄print(fruits[-2]) # 橙子这比用fruits[len(fruits)-1]简洁多了。当你不知道列表有多长但想取最后一个时-1永远正确。3. 切片取一段子列表之前切片也讲过一点如果你想取连续的一段元素比如第 1 个到第 3 个注意索引规则可以用切片语法[开始索引:结束索引]。注意结束索引不包含在内。nums [0, 1, 2, 3, 4, 5] print(nums[1:4]) # [1, 2, 3] —— 取索引 1、2、3不含索引 4切片还可以省略开始或结束[:3]表示从开头到索引 3不含 3[3:]表示从索引 3 到末尾[:]表示整个列表的副本后面会详细讲还可以加步长print(nums[::2]) # [0, 2, 4] 每隔一个取一个print(nums[::-1]) # [5, 4, 3, 2, 1, 0] 反转列表切片不会越界报错nums[1:100]会安全地返回从索引 1 到末尾的所有元素而不会崩溃。这是一个很贴心的特性。4.查找元素的索引index()如果你知道元素的值想找到它第一次出现的位置用index()方法。colors [红, 绿, 蓝, 绿]pos colors.index(绿)print(pos) # 1第一次出现的位置如果列表中有多个相同元素index()只返回第一个。如果元素不存在index()会直接抛出ValueError程序会终止。所以通常先判断一下if 黄 in colors: print(colors.index(黄)) else: print(没有黄色)5. 统计出现次数count()想知道某个值在列表中出现了几次用count()colors [红, 绿, 蓝, 绿] print(colors.count(绿)) # 2 print(colors.count(黑)) # 0不存在的返回 0不会报错6. 判断是否存在in与not in这是最常用的成员检查返回True或False。fruits [苹果, 香蕉] print(苹果 in fruits) # True print(芒果 not in fruits) # Truein是符合直觉的自然语言推荐使用。7.获取列表长度len()len()是 Python 内置函数返回列表中有多少个元素。print(len(fruits)) # 23.修改列表让数据动起来列表最大的魅力在于它是可变的。你可以改变里面的内容可以增加可以删除。1. 通过索引修改元素直接赋值即可fruits [苹果, 香蕉, 橙子] fruits[1] 草莓 print(fruits) # [苹果, 草莓, 橙子]这就像你把购物清单上的“香蕉”划掉改写成“草莓”。2. 在末尾追加append()append()在列表的最后面添加一个新元素。fruits.append(芒果) print(fruits) # [苹果, 草莓, 橙子, 芒果]3. 在中间插入insert()insert(索引, 值)可以在任意位置插入。插入后该位置及后面的元素都会向后移动一位。fruits.insert(1, 菠萝) print(fruits) # [苹果, 菠萝, 草莓, 橙子, 芒果]4. 扩展列表extend()与如果你有另一个列表或任何可迭代对象想要全部加进来用extend()。more [石榴, 桃子] fruits.extend(more) print(fruits) # [苹果, 菠萝, 草莓, 橙子, 芒果, 石榴, 桃子]也可以写成fruits more与extend效果相同。more [石榴, 桃子] fruitsmore print(fruits) # [苹果, 菠萝, 草莓, 橙子, 芒果, 石榴, 桃子]区分append和extendlst [1, 2] lst.append([3, 4]) # 把整个 [3,4] 当成一个元素加进去 print(lst) # [1, 2, [3, 4]] lst2 [1, 2] lst2.extend([3, 4]) # 把 3 和 4 分别加入 print(lst2) # [1, 2, 3, 4]append是“整箱放进去”extend是“拆箱后一个个放”。5. 修改切片一套强大的批量替换你可以用切片同时替换多个元素甚至替换成不同数量的元素nums [0, 1, 2, 3, 4] nums[1:3] [100, 200, 300] # 替换索引 1 和 2 的位置放入三个新元素 print(nums) # [0, 100, 200, 300, 3, 4]删除一段元素也简单赋值为空列表[]nums[2:5] [] # 删除索引 2~4 的元素 print(nums) # [0, 100, 4]6. 其他修改方式排序与反转reverse()原地反转顺序不返回新列表nums [1, 2, 3] nums.reverse() print(nums) # [3, 2, 1]sort()原地排序默认升序nums [3, 1, 4, 2] nums.sort() print(nums) # [1, 2, 3, 4]如果想降序nums.sort(reverseTrue)。nums.sort(reverseTrue)中的reverse是一个关键字参数命名参数它需要接收一个布尔值注意sort()和reverse()都是修改原列表不返回新列表。如果你想保留原列表用sorted()和reversed()函数。original [3, 1, 4] new_sorted sorted(original) # 不改变 original print(original) # [3, 1, 4] print(new_sorted) # [1, 3, 4]4.删除列表元素删除元素有好几种方式可以根据情况来进行选择。1. pop(索引)—— 删除并返回指定位置的元素pop()就像从一叠纸牌中抽出一张你既删除了它还能拿到它。fruits [苹果, 香蕉, 橙子, 葡萄] removed fruits.pop(1) # 删除索引 1香蕉 print(removed) # 香蕉 print(fruits) # [苹果, 橙子, 葡萄]如果不给参数pop()删除并返回最后一个元素last fruits.pop() print(last) # 葡萄 print(fruits) # [苹果, 橙子]何时用pop()你需要用到被删除的值时。2.remove(值)—— 删除第一个匹配的值如果你不知道索引只知道要删除哪个值用remove()。fruits [苹果, 香蕉, 橙子, 香蕉] fruits.remove(香蕉) # 删除第一次出现的香蕉 print(fruits) # [苹果, 橙子, 香蕉] 还剩一个香蕉如果值不存在会抛出ValueError。建议先检查if 芒果 in fruits: fruits.remove(芒果)注意remove()只删除第一个匹配项。要删除所有匹配需要循环或列表推导式后面会讲。3.clear()—— 清空整个列表fruits.clear() print(fruits) # []清空后列表变量还在空列表只是里面没东西了。4.del语句 —— 删除切片或整个变量del不是方法而是 Python 语句可以删除列表的某一段或者完全删除变量。删除切片fruits [苹果, 香蕉, 橙子, 葡萄] del fruits[1:3] # 删除索引 1 和 2 print(fruits) # [苹果, 葡萄]删除整个变量del fruits # print(fruits) # NameError: name fruits is not defined5.进阶扩展Python 列表与 C/C 数组的底层实现有相似之处但在使用层面差异很大。1.底层实现Python 列表本质上是一个动态数组内部连续存储指向PyObject的指针。它支持 O(1) 索引自动扩容预留空间。C/C 数组如int arr[10]也是连续内存块存储具体值或指针如果是指针数组。使用层面的区别特性Python 列表C/C 数组元素类型可存储任意混合类型其实是指针指向不同对象所有元素类型必须相同大小动态可变append,pop等固定大小创建后不可变内存管理自动管理垃圾回收、内存分配手动分配/释放栈上自动堆上需free索引检查自动进行边界检查越界抛出IndexError不检查边界越界是未定义行为性能稍慢因为存储的是指针多一层间接访问动态扩容有开销更快直接访问值无额外间接嵌套任意层次的嵌套列表里套列表多维数组每个维度大小固定2.复制列表浅拷贝 vs 深拷贝直接赋值不会复制列表只是给同一块内存贴了个新标签。修改新变量会同时影响原变量。a [1, 2, 3] b a # b 和 a 指向同一个列表 b[0] 99 print(a) # [99, 2, 3] —— a 也被改了如果你想要一个独立副本可以用以下方式b a.copy()b a[:]b list(a)但这些方法都是浅拷贝如果列表内部还有子列表嵌套子列表仍然共享。a [1, [2, 3]] b a.copy() b[1][0] 99 print(a) # [1, [99, 3]] —— 内部列表被影响要彻底独立需要用深拷贝import copy a [1, [2, 3]] b copy.deepcopy(a) b[1][0] 99 print(a) # [1, [2, 3]] 原列表完好无损copy是 Python 标准库提供的一个模块。deepcopy是copy模块里预定义的函数专门用来执行深拷贝操作。调用方式copy.deepcopy(a)表示从copy模块中取出deepcopy这个函数然后把a作为参数传进去。类似的copy.copy()是浅拷贝函数。3. 列表作为栈和队列栈Last-In-First-Out后进先出像叠盘子。用append()压入用pop()弹出。队列First-In-First-Out先进先出像排队。可以用append()入队pop(0)出队但pop(0)效率低。更好的做法是用collections.deque。4.用enumerate()同时获取索引和值在循环中既想要元素值又想要它的索引使用enumerate()fruits [苹果, 香蕉, 橙子] for i, fruit in enumerate(fruits): print(f{i}: {fruit}) # 输出 # 0: 苹果 # 1: 香蕉 # 2: 橙子5. 列表推导式的高级用法可以嵌套循环、多重条件# 生成 (x,y) 坐标对 coords [(x, y) for x in range(3) for y in range(2)] print(coords) # [(0,0), (0,1), (1,0), (1,1), (2,0), (2,1)] # 带条件的嵌套 pairs [(x, y) for x in range(5) for y in range(5) if x ! y]6. 将列表当作“参数包” ——*解包可以用*将列表拆解成多个参数传给函数def add(a, b, c): return a b c nums [1, 2, 3] print(add(*nums)) # 6 等同于 add(1,2,3)7. 常见陷阱与避坑指南陷阱说明正确做法索引越界lst[100]会抛出IndexError先if len(lst) 100:或用切片切片不报错遍历时删除元素跳过元素或引发错误遍历副本for x in lst[:]:或者用列表推导式过滤浅拷贝导致内部列表共享修改嵌套列表影响原对象使用copy.deepcopy()appendvsextend混淆误将列表整体添加而不是拆开需要分别添加元素用extend或忘记sorted不改变原列表sorted(lst)不修改原列表需赋值lst sorted(lst)或使用lst.sort()用而不是比较if lst [1,2]:语法错误if lst [1,2]:感谢你的观看期待我们下次再见