Day09 Python数据容器三大主角:list、str、tuple,从评委打分一路玩到斗地主发牌 Day09 Python数据容器三大主角list、str、tuple从评委打分一路玩到斗地主发牌摘要学到 Day09变量、类型、流程控制都摸过一遍了可一旦数据多起来一个变量只装一个值就开始难受。本文围绕 Python 最常用的三类数据容器——列表 list、字符串 str、元组 tuple把定义、索引、切片、常用方法、组包解包讲透再用评委打分、列表去重、列表推导式、邮箱校验、身份证解析、学生成绩单和斗地主发牌七个案例把它们串起来。读完你至少能判断这份数据该用谁装、能不能改、怎么高效取出来。一、先说清楚为什么需要数据容器前面几天的内容其实都在解决单个数据的问题字面量、变量、int/float/str/bool、运算符、输入输出、if/while/for。到这一步写个小脚本完全够用。但只要数据一多问题立刻暴露。比如要存我和 AI 大模型来回聊的十几条消息。如果还用普通变量代码会变成这样msg1你好msg2帮我写个函数msg3参数要校验msg4好的msg5再改一版# ... 后面还有几十条一个变量只装一个值消息越多变量越多命名越来越乱遍历、查找、统计更是无从下手。数据容器就是来解决这件事的它本身是一个数据类型但里面可以装很多个元素每个元素都可以是任意类型。用一个名字管理一批数据代码立刻从散装变成整装。Python 里常见的数据容器按能不能重复、能不能修改、有没有顺序分成五类列表 list字符串 str元组 tuple集合 set字典 dict这篇文章把镜头对准前三个因为它们是后面几乎所有 Python 代码的底座。二、列表 list能改、有序、能重复2.1 定义与索引列表的第一把钥匙列表用一对方括号定义元素之间用逗号隔开name_list[杨过,郭靖,黄蓉,小龙女,洪七公,欧阳锋]它的三个特点要记牢有序、可重复、可修改而且元素类型可以混着放。列表里的每个元素都有编号也就是索引正向索引从左往右从0开始反向索引从右往左从-1开始。访问、修改、删除都围绕索引展开name_list[杨过,郭靖,黄蓉,小龙女,洪七公,欧阳锋]print(len(name_list))# 6print(name_list[3])# 小龙女print(name_list[-1])# 欧阳锋name_list[3]姑姑# 修改print(name_list[3])# 姑姑delname_list[-1]# 删除print(name_list)# [杨过, 郭靖, 黄蓉, 姑姑, 洪七公]这里有个新手最容易踩的坑最大索引 列表长度 - 1。长度为 6 的列表正向上限是5写name_list[6]会直接抛IndexError。还有一个很顺手的遍历技巧既要元素又要索引时用enumerate。forindex,nameinenumerate(name_list):print(index,name)2.2 切片一次取一段列表、字符串、元组都支持切片语法是序列[开始索引:结束索引:步长]三条规则必须背下来包含开始索引不包含结束索引开始索引默认0结束索引默认到末尾步长默认1正负索引都能用步长为负数表示从后往前。s[A,B,C,D,E,F,G,H,I]print(s[2:6])# [C, D, E, F]print(s[:3])# [A, B, C]print(s[6:])# [G, H, I]print(s[::2])# [A, C, E, G, I]print(s[::-1])# 反转整个列表切片不是复制一段就完了它是后面处理字符串、提取身份证号、截文件名的通用工具值得多敲几遍。2.3 常用方法增删改查一次到位方法可以理解成对象自带的功能。列表是对象所以它天然带了一批操作自己的方法方法作用示例append(x)尾部追加元素s.append(10086)insert(i, x)在索引 i 前插入s.insert(0, 92)remove(x)删除第一个匹配到的值s.remove(75)pop(i)删除并返回索引 i 的元素默认最后一个s.pop(2)/s.pop()sort()原地排序s.sort()/s.sort(reverseTrue)reverse()原地反转s.reverse()count(x)统计元素出现次数s.count(75)names[杨过,郭靖,黄蓉,小龙女]names.append(洪七公)names.insert(1,穆念慈)names.remove(郭靖)poppednames.pop()print(names)print(被弹出的是,popped)scores[88,99,66,85,98]scores.sort()# 升序scores.sort(reverseTrue)# 降序print(scores)一个边界要知道remove删除不存在的元素会报错sort要求元素类型一致字符串和数字混着排会失败。2.4 案例一评委打分去掉最高最低需求很具体10 位评委打 0-100 的分去掉一个最高分、一个最低分剩下 8 个求平均。思路拆成四步先建空列表 → 循环录入 10 个分数 → 求总分/最高/最低 → 套公式。score_list[]foriinrange(1,11):scoreint(input(f请输入第{i}位评委的打分: ))score_list.append(score)totalsum(score_list)highestmax(score_list)lowestmin(score_list)final_score(total-highest-lowest)/(len(score_list)-2)print(f去掉一个最高分{highest}、一个最低分{lowest})print(f选手最终得分{final_score:.2f})这里顺手认识了四个内置函数sum()求和、max()取最大、min()取最小、len()取长度。它们比手写循环更简洁也更不容易出错。2.5 案例二两个列表合并后去重合并可以直接用也可以用解包*去重则要自己判重因为列表允许重复。list1[19,23,54,64,875,20,109,232,123,54]list2[55,80,72,35,60,123,54,29,91]# 合并mergedlist1list2# 等价写法merged [*list1, *list2]# 去重result[]fornuminmerged:ifnumnotinresult:# in 判断元素是否存在result.append(num)print(result)核心就一句先查再放。in运算符返回布尔值存在是True不存在是False。这段代码后面学集合时还能再简化但在列表阶段这是最直观的解法。2.6 列表推导式把循环压缩成一行先看传统写法生成 1-20 的平方列表num_list[]fornuminrange(1,21):num_list.append(num**2)逻辑没问题但列表推导式可以写得更紧凑num_list[num**2fornuminrange(1,21)]再加上过滤条件比如从一堆数字里挑出偶数并求平方nums[12,32,45,77,80,92,33,57,97,98,110,111,122]result[n**2forninnumsifn%20]print(result)两种语法格式记一下[新值表达式for元素in数据容器][新值表达式for元素in数据容器if条件]推导式不是炫技它在筛选 转换场景里确实更清晰。但如果逻辑一复杂还是老老实实写循环更好读。三、字符串 str不可变但操作很丰富3.1 基本操作索引、切片、不可变字符串本质是字符的容器有顺序、可迭代、支持索引和切片但不可变。sPythonprint(s[0])# Pprint(s[-1])# nprint(s[0:5])# Pythoprint(s[::2])# Ptoprint(s[::-1])# nohtyP不可变的意思是你不能原地改某个字符。下面这行会直接报错s[0]J# TypeError想改只能生成一个新字符串比如s J s[1:]。这跟列表的原地改是两种完全不同的思路写代码时要分清楚。3.2 常用方法查找、切割、替换、判断字符串的方法很多常用的这批足够覆盖大部分日常需求方法作用find(sub)返回子串第一次出现的索引找不到返回 -1count(sub)统计子串出现次数upper()/lower()转大写 / 转小写split(sep)按分隔符切成列表strip(chars)去掉两端空白或指定字符replace(old, new)替换子串startswith(sub)/endswith(sub)判断开头 / 结尾sHello Worldprint(s.find(World))# 6print(s.count(l))# 3print(s.upper())# HELLO WORLDprint(s.lower())# hello worldprint(s.split( ))# [Hello, World]print( zhangsan .strip())# zhangsanprint(s.replace(World,Python))print(s.startswith(Hello))# True注意这些方法都不会修改原字符串而是返回一个新字符串。这是不可变的直接体现。3.3 案例邮箱校验、文件名拆分、身份证解析案例一邮箱格式验证规则包含一个并且至少有一个.。mailinput(请输入邮箱: )ifmail.count()1and.inmail:print(邮箱格式正确)else:print(邮箱格式错误)count管数量in管存在性两个条件用and串起来一行就完成了校验。案例二从avatar.png拆出文件名和后缀点号位置是 6前面是名字后面是后缀img_nameavatar.pngnameimg_name[0:6]suffiximg_name[6:]print(name)# avatarprint(suffix)# .png这题的价值在于它把切片从抽象语法变成了真实任务。以后学split(.)会更省事但先手写一遍切片理解会更扎实。案例三从身份证号提取出生日期和性别身份证号里[6:14]是出生日期倒数第 2 位是性别位奇男偶女id_card440304198512126029yearid_card[6:10]monthid_card[10:12]dayid_card[12:14]sex_numint(id_card[-2])sex女ifsex_num%20else男print(f出生日期{year}年{month}月{day}日)print(f性别{sex})这里顺带用了一次三元运算符值1 if 条件 else 值2。它适合这种二选一的简单赋值逻辑一复杂还是写if...else更稳。四、元组 tuple定义完就不许改4.1 基本操作不可变的序列元组和列表长得很像但核心区别只有一句话元组一旦定义不能修改。t(杨过,郭靖,黄蓉,小龙女)print(t[1])# 郭靖print(t[-1])# 小龙女print(t[0:2])# (杨过, 郭靖)print(t.count(郭靖))# 1print(t.index(郭靖))# 1它支持索引、切片、遍历也支持count和index但修改、删除一律不行t[0]张三丰# TypeError定义元组还有几个常见写法t1(5,7,9)t25,7,9# 省略括号也行本质是组包t3()# 空元组t4tuple()t5(A,)# 单元素元组逗号不能省尤其是t5 (A,)这个逗号很多人第一次都会写成(A)结果它只是加了括号的字符串。4.2 组包与解包把数据拆开、再收拢组包 Packing把多个值合并进一个容器。解包 Unpacking把容器拆开分别赋给多个变量。# 组包user_info张三,18,湖北武汉,zhangsan163.com# 基础解包name,age,address,emailuser_info# 扩展解包* 收集剩余元素name,age,*infouser_infoprint(name,age,info)# 张三 18 [湖北武汉, zhangsan163.com]name,*info,emailuser_infoprint(info)# [18, 湖北武汉]*的作用是把剩下的一堆打包成列表。这个语法在处理前几个固定、后面不定长的数据时特别好用。4.3 案例一学生成绩单分析需求有三件事算每个学生的总分和平均分、统计各科的最低/最高/平均、筛出平均分大于 90 的学生。数据用元组的元组来存天然适合这种不该被改动的成绩单students((S001,王林,85,92,78),(S002,李慕婉,92,88,95),(S003,十三,78,85,82),(S004,曾牛,88,79,91),(S005,周轶,95,96,89),)# 每个学生的总分与平均分forstudentinstudents:stu_no,name,chinese,math,englishstudent totalchinesemathenglish avgtotal/3print(stu_no,name,total,f{avg:.2f})# 各科成绩列表列表推导式 解包chinese_scores[s[2]forsinstudents]math_scores[s[3]forsinstudents]english_scores[s[4]forsinstudents]forsubject,scoresin[(语文,chinese_scores),(数学,math_scores),(英语,english_scores)]:print(subject,min(scores),max(scores),sum(scores)/len(scores))# 平均分大于 90 的学生forstudentinstudents:stu_no,name,chinese,math,englishstudent avg(chinesemathenglish)/3ifavg90:print(优秀,stu_no,name,f{avg:.2f})这个案例几乎把前面的知识点全串了一遍元组、解包、循环、条件判断、列表推导式、min/max/sum/len。写完它数据容器的组合拳基本就有感觉了。4.4 案例二斗地主做牌、洗牌、发牌最后一个案例有点意思用列表和元组做一个简易斗地主。点数用元组numbers花色用元组colors做好的 54 张牌放进列表card_list洗牌用随机下标交换发牌用切片。importrandom numbers(A,2,3,4,5,6,7,8,9,10,J,Q,K)colors(H,D,S,C)# 红桃/方片/黑桃/梅花card_list[]forcolorincolors:fornumberinnumbers:card_list.append(colornumber)card_list.append(大王)card_list.append(小王)# 洗牌遍历每一张牌和随机位置的牌交换fori,cardinenumerate(card_list):jrandom.randint(0,len(card_list)-1)card_list[i],card_list[j]card_list[j],card_list[i]# 发牌三位玩家各 17 张剩 3 张底牌forindex,playerinenumerate([玩家1,玩家2,玩家3]):handcard_list[index:-3:3]print(player,hand)print(底牌,card_list[-3:])做牌是嵌套循环洗牌是列表元素交换发牌是切片加步长。每一步都在用前面学过的容器操作属于那种看着简单、写起来很爽的综合练习。五、Day09 技术栈总结把 list、str、tuple 放在一起对比选型思路就清楚了特性list 列表str 字符串tuple 元组可否修改可以不可以不可以是否有序是是是是否可重复是是是索引 / 切片支持支持支持典型场景批量数据、增删改查文本处理固定配置、只读数据再补几条实用建议需要频繁增删改用list数据定下来就不该变用tuple相当于给自己上一层保险处理文本优先想str的方法别一上来就手写循环判断该用谁先问自己一句这份数据以后要不要改六、写在最后Day09 的内容密度不小三种容器、索引、切片、两组方法表、组包解包再加上七个案例。刚学完会觉得零碎但只要动手把它们写一遍很多概念会自然连起来。我的建议是别急着背方法表先抓住三个核心问题这份数据能不能改——决定用 list 还是 tuple/str。我要按位置取还是按规则筛——决定用索引/切片还是推导式。数据是一个整体还是一组独立值——决定要不要解包。把这三个问题想清楚方法名忘了随时查就行思路不会乱。感谢阅览如果这篇对你有帮助欢迎点赞、收藏、关注。代码这东西看十遍不如自己敲一遍。评论区欢迎聊聊你踩过的容器坑。