在Python的世界里,列表(list)绝对是“国民级工具”——就像你书桌抽屉里的万能收纳盒,既能塞零散的笔,也能放整叠的纸,还能混装便签和U盘。新手觉得它“简单到不值一提”,老手却能把它玩出百般花样,堪称“入门必学、进阶必备”的神器。今天咱们就来扒一扒列表的底细,从基础操作到高级玩法,用最接地气的比喻把技术讲透,让你看完既能搞定实操,又能在面试时露一手。
一、基础功能:把收纳盒用明白
先明确核心定位:列表是Python中最灵活的“有序可变容器”。“有序”意味着元素都有专属“索引”(座位号),“可变”则允许你随时增减、替换元素——这和元组(tuple)那个“密封收纳盒”截然不同,列表主打的就是一个随心所欲。
1. 创建列表:给收纳盒“开光”
创建列表就像入手一个新收纳盒,你可以让它空着,也能一到手就装满东西。最常用的两种方式,简单到闭着眼都能写对:
第一种是“直接塞东西”,用**方括号[]**把元素括起来,元素之间用逗号隔开,堪称“懒人专属”:
# 装水果的收纳盒
fruit_box = ["苹果", "香蕉", "橙子"]
# 混装的收纳盒(列表不挑元素类型,万物皆可装)
mix_box = [123, "Python", True, [1,2,3]] # 里面还能套个小收纳盒(嵌套列表)
第二种是用list()函数“批量装东西”,适合把其他“容器”(比如字符串、元组)里的东西转进列表:
# 把字符串拆成单个字符,装进列表
str_to_list = list("Python") # 结果:['P','y','t','h','o','n']
# 把元组转成列表(解锁“修改权限”)
tuple_to_list = list(("猫", "狗", "猪")) # 结果:['猫','狗','猪']
这里插个小吐槽:新手常踩的小坑是末尾多写逗号,比如[1,2,]——虽然Python不会报错,但就像收纳盒里留了个空位置,看着格外别扭,规范写法建议去掉末尾逗号。
2. 访问元素:精准找到“某件东西”
列表里的每个元素都有专属“座位号”(索引),就像收纳盒上的格子编号。重点提醒!Python的索引是“从0开始计数”的——这就像电影院座位从1排起,Python偏要从0排算,刚开始难免绕晕,多练几次就习惯了。
fruit_box = ["苹果", "香蕉", "橙子"]
print(fruit_box[0]) # 取第0个元素:苹果
print(fruit_box[2]) # 取第2个元素:橙子
除了正向索引,还能使用反向索引——从收纳盒最右侧开始计数,-1代表最后一个元素,-2代表倒数第二个,特别适合不知道列表长度时快速抓取末尾元素。
print(fruit_box[-1]) # 倒数第一个:橙子
print(fruit_box[-2]) # 倒数第二个:香蕉
若索引超出列表范围,Python会直接抛出IndexError错误,就像非要从只有3个格子的收纳盒里找第5格的东西,自然是竹篮打水。这里分享个避坑小技巧:先用 len() 函数获取列表长度,再访问元素,就能有效规避报错。
box_length = len(fruit_box) # 3
if0 <= index < box_length:
print(fruit_box[index])
else:
print("索引越界啦!")
3. 切片:批量“拿一堆东西”
如果想从收纳盒里取出连续的一堆东西,逐个访问太过繁琐,这时“切片”功能就能派上用场——相当于用一把尺子划定范围,从指定位置切到另一处,把中间的元素一次性取出。切片语法为:列表[起始索引:结束索引:步长],三个参数均可省略,灵活度拉满。
先讲最基础的“起始+结束”组合,务必记住:切片遵循“左闭右开”规则,即包含起始索引对应的元素,不包含结束索引对应的元素。这就像你说“拿第0到第2个元素”,实际只能拿到第0和第1个,这个坑一定要记死。
fruit_box = ["苹果", "香蕉", "橙子", "葡萄", "芒果"]
# 取第1到第3个元素(不包含3),结果:["香蕉", "橙子"]
print(fruit_box[1:3])
# 省略起始索引,从第0个切到第3个:["苹果", "香蕉", "橙子"]
print(fruit_box[:3])
# 省略结束索引,从第2个切到末尾:["橙子", "葡萄", "芒果"]
print(fruit_box[2:])
再来说说“步长”,它相当于“每隔几个元素取一个”,默认步长为1(即连续取值)。步长设为2时,会跳一个元素取一个;步长为负数时,还能实现反向切片(从右往左取值)。
# 步长为2,跳一个拿一个:["苹果", "橙子", "芒果"]
print(fruit_box[::2])
# 反向切片,步长为-1,相当于反转列表:["芒果", "葡萄", "橙子", "香蕉", "苹果"]
print(fruit_box[::-1])
# 从第3个元素反向切到第1个:["葡萄", "橙子"]
print(fruit_box[3:1:-1])
切片还有个贴心特性:即便索引越界也不会报错,只会返回能取到的元素,就像从收纳盒里多切了一刀,最多只能拿到盒子里现存的东西,不会凭空变出元素。
4. 增删改:给收纳盒“换内容”
列表的“可变”特性,核心就体现在增删改操作上——如同收纳盒能随时添东西、扔废物、换物品,操作起来灵活自如。
先讲“修改”:直接通过索引赋值,把旧元素换成新元素,简单粗暴:
fruit_box = ["苹果", "香蕉", "橙子"]
fruit_box[1] = "榴莲"# 把第1个元素换成榴莲
print(fruit_box) # 结果:["苹果", "榴莲", "橙子"]
新增元素有三种常用方法,分别适配不同场景,咱们逐一说明:
- **append()**:在列表末尾添加单个元素,相当于把东西直接扔到收纳盒最底层,执行效率最高,推荐优先使用:
fruit_box.append("葡萄") # 结果:["苹果", "榴莲", "橙子", "葡萄"]
- **insert()**:在指定索引位置插入元素,相当于在收纳盒中间塞进一件东西,后续元素都要依次后移——这种方式效率偏低,列表越长,元素后移的成本越高,非必要不建议用:
fruit_box.insert(2, "芒果") # 在第2个位置插芒果
print(fruit_box) # 结果:["苹果", "榴莲", "芒果", "橙子", "葡萄"]
- **extend()**:将另一个列表的所有元素批量添加到当前列表末尾,相当于把另一个收纳盒的东西全倒进来。它和append()的核心区别在于:append()会把整个列表当作单个元素添加:
other_fruits = ["西瓜", "菠萝"]
fruit_box.extend(other_fruits) # 批量添加
print(fruit_box) # 结果:["苹果", "榴莲", "芒果", "橙子", "葡萄", "西瓜", "菠萝"]
# 对比append()的效果
fruit_box.append(other_fruits) # 把列表当一个元素加进去
print(fruit_box) # 末尾会多一个列表:[..., ["西瓜", "菠萝"]]
删除元素同样有四种常用方法,可根据实际需求选择:
- del:按索引删除元素,也能直接删除整个列表,相当于要么拆掉收纳盒的某个格子,要么把整个收纳盒扔掉:
del fruit_box[1] # 删除第1个元素(榴莲)
del fruit_box # 删除整个列表,之后再访问会报错
- **pop()**:按索引删除元素,且会返回被删除的元素,相当于把东西从收纳盒里拿出来,还能当场看清拿的是什么——默认删除列表最后一个元素:
deleted_fruit = fruit_box.pop() # 删除最后一个元素(菠萝),返回“菠萝”
deleted_fruit = fruit_box.pop(2) # 删除第2个元素(芒果),返回“芒果”
- **remove()**:按元素值删除对应元素,相当于在收纳盒里找特定物品,找到后直接扔掉。需注意:若存在重复元素,仅删除第一个;若元素不存在,会抛出报错:
fruit_box.remove("橙子") # 删除“橙子”
# 重复元素的情况
num_box = [1,2,3,2,4]
num_box.remove(2) # 只删第一个2,结果:[1,3,2,4]
- **clear()**:清空列表内所有元素,相当于把收纳盒里的东西全倒空,只留下空盒子本身:
fruit_box.clear() # 结果:[]
二、高级用法:把收纳盒玩出花
学会基础操作,只能算“会用收纳盒”;掌握高级玩法,才算真正“玩明白列表”。这部分内容不仅能提升代码执行效率,还能让你的代码更简洁优雅,面试时被问到也能从容应答。
1. 列表推导式:一键“批量造元素”
如果想创建有规律的列表(比如1到10的平方、筛选偶数),用普通循环要写三四行代码,而列表推导式一行就能搞定——相当于给收纳盒装东西时启用流水线批量生产,无需逐个摆放,简洁又高效,更是Python程序员的必备炫技神器。
其基础语法为:[表达式 for 变量 in 可迭代对象 if 条件判断],通俗来讲就是:遍历可迭代对象中的每个变量,若满足设定条件,就执行表达式并将结果存入列表。
先看个简单例子:创建1到10的平方列表,用循环和推导式对比:
# 普通循环
square_list = []
for i in range(1, 11):
square_list.append(i*i)
# 列表推导式
square_list = [i*i for i in range(1, 11)]
print(square_list) # 结果:[1,4,9,...,100]
再加上条件判断,筛选出1到10中的偶数平方:
even_square = [i*i for i in range(1,11) if i%2 == 0]
print(even_square) # 结果:[4, 16, 36, 64, 100]
还能写“嵌套推导式”,相当于多层循环,比如创建一个2x3的二维列表(嵌套列表):
two_d_list = [[i*j for j in range(1,4)] for i in range(1,3)]
print(two_d_list) # 结果:[[1,2,3], [2,4,6]]
这里提醒一句:列表推导式虽好用,但别过度复杂化。若嵌套超过两层,代码可读性会急剧下降,就像把收纳盒堆成小山,后续找东西反而更麻烦。
2. 匿名函数+列表:灵活“处理元素”
将 匿名函数(lambda) 与 map() 、filter() 函数结合,能快速实现列表元素的批量改造与筛选,相当于给收纳盒里的所有东西做个专属SPA——无需编写复杂函数,一行代码就能搞定。
map()函数:
它会对列表中的每个元素执行同一操作,相当于给收纳盒里的每件物品都贴一张统一标签。
num_list = [1,2,3,4,5]
# 用lambda函数把每个元素乘2,map()返回迭代器,转成列表
double_list = list(map(lambda x: x*2, num_list))
print(double_list) # 结果:[2,4,6,8,10]
filter()函数
它会按设定条件筛选元素,相当于把收纳盒里的东西逐一筛选,不符合条件的直接剔除。
# 筛选出大于3的元素
big_num = list(filter(lambda x: x>3, num_list))
print(big_num) # 结果:[4,5]
可能有人会问:这和列表推导式不就是重复功能吗?其实二者各有优势:简单场景下,列表推导式可读性更强;复杂场景中,map()和filter()可组合多个函数使用,灵活性更高。比如同时实现“元素乘2+筛选大于5的结果”,两种写法都能实现。
# 列表推导式
result = [x*2for x in num_list if x*2 >5]
# map+filter
result = list(filter(lambda x: x>5, map(lambda x: x*2, num_list)))
两种写法都能得到结果[6,8,10],按需选择即可。
3. 列表的“隐藏技能”:排序、计数、反转
除了基础的增删改查,列表还有几个实用的“隐藏技能”,无需手动编写逻辑,调用对应方法就能实现。
首先是排序功能,有两种常用方式:sort() 和 sorted() 。前者是“原地排序”,直接修改原列表,相当于把收纳盒里的东西重新按顺序摆放;后者会生成新列表,原列表保持不变,相当于复印一份再排序。
num_list = [3,1,4,2,5]
# sort()原地排序,默认升序
num_list.sort()
print(num_list) # 结果:[1,2,3,4,5]
# 降序排序
num_list.sort(reverse=True)
print(num_list) # 结果:[5,4,3,2,1]
# sorted()生成新列表,原列表不变
new_list = sorted(num_list, reverse=True)
print(new_list) # 降序新列表
print(num_list) # 原列表不变
这里要注意:sort()只支持元素类型一致的列表排序,比如全是数字或全是字符串,混装不同类型元素的列表排序会直接报错——就像要给苹果和书本按大小排序,根本无法比较。
其次是 计数 功能:count() 方法可统计指定元素在列表中的出现次数,相当于数清楚收纳盒里有多少件相同的物品。
num_list = [1,2,2,3,2,4]
print(num_list.count(2)) # 结果:3(2出现了3次)
最后是反转功能:reverse() 方法会原地反转列表顺序,相当于把收纳盒倒扣过来,里面的东西顺序完全颠倒。
fruit_box = ["苹果", "香蕉", "橙子"]
fruit_box.reverse()
print(fruit_box) # 结果:["橙子", "香蕉", "苹果"]
值得一提的是,反转列表也能用切片[::-1]实现,这种方式会生成新列表,不会改变原列表内容,可根据需求选择。
4. 列表作为栈和队列:一物两用
列表的特性使其能轻松模拟“栈”和“ 队列”两种数据结构,相当于一个收纳盒既能用来“叠盘子”(对应栈),又能用来“排队买票”(对应队列),实现一物两用。
栈(LIFO:后进先出):就像叠盘子,最后放上去的盘子要最先拿下来。借助 append() (从末尾添加)和 pop() (从末尾删除)就能实现,执行效率极高。
stack = []
# 入栈(叠盘子)
stack.append("盘子1")
stack.append("盘子2")
stack.append("盘子3")
# 出栈(拿盘子)
print(stack.pop()) # 盘子3(最后放的先拿)
print(stack.pop()) # 盘子2
队列(FIFO:先进先出):就像排队买票,最先排队的人能最先购票。可用append() (从末尾添加)和pop(0)(从开头删除)实现,但要注意:pop(0)效率较低,删除开头元素后,后续所有元素都要向前移动。若队列元素较多,推荐使用collections.deque,效率会大幅提升。
queue = []
# 入队(排队)
queue.append("人1")
queue.append("人2")
queue.append("人3")
# 出队(买票)
print(queue.pop(0)) # 人1(最先排的先买)
print(queue.pop(0)) # 人2
# 高效队列(推荐)
from collections import deque
queue = deque()
queue.append("人1")
queue.popleft() # 出队,效率比pop(0)高得多
5. 列表深浅拷贝:避免“牵一发而动全身”
这是新手最容易踩的坑,没有之一:列表赋值时默认是“引用赋值”,相当于给同一个收纳盒贴了张新标签,并非复制出一个全新收纳盒——修改新标签对应的列表,原列表内容也会跟着改变。
a = [1,2,3]
b = a # 引用赋值,不是拷贝
b[0] = 100# 修改b
print(a) # 结果:[100,2,3](a也变了)
要解决这个问题,就需要用到“拷贝”功能,具体分为浅拷贝和深拷贝两种场景:
浅拷贝:仅复制列表的外层元素,相当于复制了一个收纳盒,但盒内的“小收纳盒”(嵌套列表)仍与原列表共享。这种方式适合列表元素均为不可变类型(如数字、字符串)的场景。
a = [1,2,[3,4]]
# 浅拷贝方式1:切片
b = a[:]
# 浅拷贝方式2:list()函数
b = list(a)
# 修改外层元素,a不变
b[0] = 100
print(a) # 结果:[1,2,[3,4]]
# 修改嵌套列表(内层元素),a也变
b[2][0] = 300
print(a) # 结果:[1,2,[300,4]]
深拷贝:会完全复制整个列表,包括嵌套的内层元素,相当于复制了一个收纳盒,连盒内的小收纳盒也一并复制,与原列表完全独立。这种方式适合存在嵌套列表的场景,需导入copy模块实现。
import copy
a = [1,2,[3,4]]
b = copy.deepcopy(a) # 深拷贝
b[2][0] = 300
print(a) # 结果:[1,2,[3,4]](a不变)
总结一下:若列表无嵌套元素,浅拷贝即可满足需求;若存在嵌套元素,务必使用深拷贝,避免出现“牵一发而动全身”的问题。
三、避坑指南:这些错误别再犯了
讲完用法,咱们再来盘点几个新手常犯的错误,帮你提前避坑:
索引越界:忘记索引从0开始,或访问的索引超出列表长度。建议用len() 函数先确认列表长度,再访问元素,可有效避免报错。
混淆append()和extend() :二者核心区别在于,append() 添加整个元素, extend() 批量添加另一个列表的元素。不确定时可打印结果对比,快速分清用法。
误将引用赋值当拷贝:直接用=赋值,导致修改新列表时影响原列表。需根据是否有嵌套元素,选择浅拷贝或深拷贝。
用列表实现队列效率低:pop(0)会导致后续元素整体前移,元素越多效率越低。元素较多时,建议用collections.deque替代。
列表推导式过度复杂:嵌套超过两层会严重降低代码可读性,增加后期维护成本。建议嵌套层数不超过两层,复杂逻辑可拆成普通循环。
四、总结:列表的“万能属性”
Python列表就像一个万能收纳盒,基础功能能满足日常“装东西、取东西”的需求,高级用法则能实现批量处理、数据结构模拟等复杂场景,是Python中最常用、最灵活的数据类型之一。
新手阶段,先把基础的增删改查、切片操作练扎实,就能应对大部分开发场景;进阶阶段,掌握列表推导式、深浅拷贝、栈与队列模拟等用法,能让你的代码更高效、更优雅。记住:技术的核心是解决问题,列表的用法再多样,最终都是为了让代码更简洁、更易维护。
最后留个小练习:用列表推导式筛选出1到100中的质数,再用sort() 排序。动手实操才能真正掌握,如果你踩了坑,欢迎在评论区留言,咱们一起交流解决!