news 2026/9/18 17:36:05

Python第四天:条件判断与字典,从顺序代码到实际程序

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python第四天:条件判断与字典,从顺序代码到实际程序

学Python到第四天,是一个很微妙的节点。前三天你大概已经知道了变量、数据类型、列表、元组这些"积木"长什么样,但写出来的代码多半是直来直去的顺序结构——从上往下执行,没有任何分支,遇到重复的事情只能复制粘贴。再往下学,如果不引入条件判断和字典,你会发现自己被困在一个很尴尬的境地:能看懂代码,但写不出能解决实际问题的程序。

条件判断(if/elif/else)让代码有了"大脑",可以根据不同情况走不同逻辑;字典(dict)则是最符合人类思维习惯的数据容器,存名字、属性、配置项都靠它。这两个知识点单独拎出来都不难,但把它们组合起来,才是Python日常开发的真正起点。这篇就按我自己的学习节奏,把第四天的重点、容易踩的坑和实战用法完整过一遍。

1. 条件判断不只是if-else:从执行流程说起

1.1 为什么前三天写的代码总感觉"没头脑"

先回忆一下,前三天写的代码长什么样?大概率是这样的:

name = "张三" age = 17 print(f"姓名:{name},年龄:{age}")

这种代码最大的问题就是"一根筋"——无论输入什么,结果都一样。但现实世界里的业务逻辑几乎全是条件性的:用户年龄大于18岁才允许注册,库存大于0才显示购买按钮,成绩大于等于60分才算及格。没有if,这些规则全部写不出来。

条件判断的本质,是让程序在某个节点上"看一眼"当前的数据状态,然后决定接下来走哪条路。这个"看一眼"的动作在Python里就是布尔表达式求值——True走一条路,False走另一条路。

1.2 从if-elif-else看代码块与缩进的关联

Python的缩进不是风格问题,而是语法的一部分。别的语言用花括号{}圈定代码块,Python用缩进层级表示"哪些代码属于同一个块"。

score = 85 if score >= 90: print("优秀") elif score >= 80: print("良好") elif score >= 60: print("及格") else: print("不及格")

这段代码的执行逻辑是这样的:程序从上往下依次判断每个条件,一旦某个条件为True,就执行它下面的缩进代码块,执行完直接跳过整个if-elif-else结构,哪怕后面的条件也成立也不再检查。

这里有个新手最容易懵的点:为什么写成elif score >= 80而不是独立写三个if?区别在于,独立if会被依次检查,可能出现多个分支都被执行;而elif是"否则如果"的缩写,本质上是"前一个不成立的前提下才检查这一个"。如果你把上面改成三个独立if,score=85时会同时进入score >= 80的"良好"分支,但score >= 90为False就不会进——看起来结果一样,但如果age=95,三个独立if会打印"优秀""良好""及格"三行,而elif链只会打印"优秀"一行。

1.3 布尔求值与短路行为:容易被忽略的细节

条件判断的底层是布尔运算。Python里的布尔值只有TrueFalse,但任何对象都可以参与真假判断——隐式转换规则是:数字0、空字符串""、空列表[]、空字典{}、None都被视为False,其他一律为True。这个规则在做数据校验时极其好用:

data = {} if not data: print("字典是空的,需要初始化")

这一行比if len(data) == 0更符合Python的惯用风格。

再说说andor的短路求值,这是从C语言时代就传下来的特性,但很多教程讲得太浅。A and B在A为False时直接返回A,不再计算B;A or B在A为True时直接返回A,不再计算B。这意味着你可以在and里安全地调用"可能不存在的东西":

user = {"name": "李四", "age": 0} if user.get("age") and user["age"] >= 18: print("已成年") else: print("未成年或年龄未知")

user.get("age")返回0时,0被当作False,所以不会进入and后面的user["age"] >= 18判断。在很多实际场景里,短路求值能避免NoneType报错这类问题,先把业务逻辑写对,再去考虑健壮性。

2. 字典的底层逻辑:为什么它查找飞快

2.1 从哈希表理解键的唯一性

字典是Python里应用最广的数据结构,没有之一。它的底层是哈希表——你可以把哈希表理解成一个带有无数编号格子的储物柜,每个格子有一个编号,你存东西的时候,用哈希函数把钥匙换算成编号,东西就放进那个格子里;找东西的时候,同样用钥匙换算成编号,直接去那个格子取。

这就是为什么字典查找是O(1)级别的——不管字典里有10个键还是10万个键,查找速度都差不多。而列表的查找是O(n)的,数据量大了差距极其明显。

这个底层机制直接推导出两个重要结论:

  • 键必须是可哈希的,也就是不可变类型。字符串、整数、元组都可以当键;列表、字典本身不能当键。
  • 键不能重复,因为同一个格子只能放一个东西。后赋值的键会覆盖前面的值。
d = {} d["name"] = "张三" d["name"] = "王五" print(d) # {'name': '王五'}

很多初学者不理解为什么Python的字典会覆盖而不是报错,理解了哈希表就明白了——同一个键经过哈希函数换算,永远得到同一个格子的编号,往同一个格子里写两次,后面的自然覆盖前面的。

2.2 字典的增删改查:从最常用的写法说起

字典最基本的操作是增删改查,但Python提供了几种不同的"查"法,用错场景会带来完全不同的代码体验。

直接取键d[key]:如果键不存在,直接抛出KeyError,程序崩溃。

user = {"name": "张三"} # print(user["age"]) # KeyError: 'age'

get()方法:键不存在时返回默认值(默认为None),不报错,是实际项目里最常用的安全取值方式。

age = user.get("age", 18)

setdefault()方法:这个方法的用法比较特别——如果键存在,返回它的值;如果键不存在,先设置默认值,再返回这个默认值。"有则取之,无则设之"的语义在计数器场景里很好用:

word_count = {} word = "python" word_count[word] = word_count.get(word, 0) + 1

但更优雅的是setdefault配合列表的场景:

courses = {} course_name = "数学" courses.setdefault(course_name, []).append("张三") courses.setdefault(course_name, []).append("李四") print(courses) # {'数学': ['张三', '李四']}

这比先判断键在不在再决定是新建列表还是追加列表要简洁得多。

2.3 字典推导式与迭代:让代码更Pythonic

列表推导式很多人知道了,字典推导式其实同样好用。假设你有一个包含多个学生的列表,想快速建立一个"名字 -> 成绩"的映射:

students = [("张三", 88), ("李四", 92), ("王五", 70)] scores = {name: score for name, score in students} print(scores) # {'张三': 88, '李四': 92, '王五': 70}

如果你还想在里面加条件判断,可以直接在后面接if:

passed = {name: score for name, score in students if score >= 80} # {'张三': 88, '李四': 92}

迭代字典时,默认只迭代键。如果你想要键值对,用items();只想要值,用values();只想遍历键并同时修改键值,就在items()上解包。这三者之间的区别,一开始就要从语义上分清楚,否则后面很容易晕。

3. 条件判断+字典的经典组合:从成绩等级系统开始

3.1 用字典存数据,用条件判断处理逻辑

前面的知识点都是散的,现在把它们串起来。一个非常典型的场景就是"学生成绩等级判定"——用字典存学生数据,用条件判断映射成绩等级。

先用原始的条件判断链实现:

students = { "张三": 88, "李四": 92, "王五": 70, "赵六": 55, } def get_grade(score): if score >= 90: return "A" elif score >= 80: return "B" elif score >= 70: return "C" elif score >= 60: return "D" else: return "E" for name, score in students.items(): grade = get_grade(score) print(f"{name}:{score}分,等级{grade}")

这段代码功能没问题,但当等级规则一多、阈值一变,修改起来就很痛苦——每加一个等级你就得在函数里加一个elif分支。

3.2 查表法:用字典映射替代长elif链

这里要分享一个很快就能用上的优化思路:当你的条件判断是"根据一个数值落在哪个区间返回对应的值"这种结构时,完全可以换一种写法——把区间边界和结果放进一个字典,然后用循环遍历。

grade_table = [ (90, "A"), (80, "B"), (70, "C"), (60, "D"), ] def get_grade(score): for threshold, grade in grade_table: if score >= threshold: return grade return "E"

这样调整等级阈值时,只需要改grade_table里的数字,函数代码一行都不用动。如果哪天规则变成"85分以上是A",改一个元组就够了,不需要在代码里翻找elif。

再进一步,如果只是"精确匹配"而不是"区间匹配",连循环都不用,直接查字典:

role_permissions = { "admin": ["create", "delete", "edit"], "editor": ["create", "edit"], "viewer": ["view"], } def can(user_role, action): return action in user_permissions.get(user_role, [])

这种查表法在处理枚举类映射、权限配置、状态机转换时极其常见。判等条件越多,字典映射的优势越明显。

3.3 边界情况:空字典、None、缺失键

组合使用条件判断和字典时,最危险的往往不是正常逻辑,而是边界情况。比如一个学生没成绩,存在字典里的值是None,你直接用score >= 60判断就会报TypeError;一个学生不在字典里,你用students["新同学"]拿成绩会报KeyError

稳妥的写法是先用in或者get做存在性检查:

def judge_student(name): score = students.get(name) if score is None: return "该学生没有成绩记录" return get_grade(score)

这里先处理None再进入条件判断,顺序很重要。Python的None is None判断是安全的,但如果你写成if not score,当分数恰好是0分时就会被误判成"没有成绩"。这个坑非常隐蔽但非常常见——布尔假值和"值为None"是两个不同的概念。

4. 新手最容易栽的坑:条件判断与字典的隐蔽错误

4.1 缩进错位导致的逻辑偏差

Python的缩进决定代码归属。哪怕条件判断本身写对了,缩进错一位,逻辑就完全变了。最经典的坑是这样的:

income = 5000 if income > 8000: gate = "高收入" elif income > 3000: gate = "中等收入" print(f"当前收入层级判断完成") # 注意这一句的缩进

这段代码的printif平级,所以无论如何都会执行。但如果你不小心把print缩进到elif里面,那就只有进入"中等收入"分支时才会打印——这种错误编译器不会报错,结果和预期完全不符。

我的建议是:在编辑器里打开"显示空格/制表符"功能,全程只用空格缩进,并且统一用4个空格。不要混用Tab和空格,这是无数新手的血泪教训——同一个文件中Tab和空格混用,Python解释器会直接报IndentationError,而且报错位置往往和真实错误位置偏差几行,很误导人。

4.2 键存在性检查:in、get()、try/except三选一

判断字典里有没有某个键,有三种常见写法,各自适用场景不同:

写法适用场景注意点
if key in d:只需要判断键存不存在语义最清晰,但会查两次表(先in再取值)
value = d.get(key, default)不存在时需要一个兜底值一次查询搞定,最常用
try: value = d[key] except KeyError:绝大多数情况键要存在,异常是意外符合"异常用于异常情况"的原则,性能也不错

很多教程喜欢推崇try/except,但在日常业务逻辑里,get通常就够用了。只有在"键如果不存在,说明数据有问题,需要特殊处理或记录日志"的情况下,才建议用try/except。不要盲目追求"防一切错误",代码的可读性和意图清晰度更重要。

另外提醒一个细节:dict.get(key)在键不存在时返回None,但如果某个键的值本身就是None,你就没法区分"键不存在"和"键存在但值是None"。如果你确实需要区分这两种状态,用in判断更保险。

4.3 循环遍历时修改字典:一个必踩的运行时错误

假如你想从字典里删掉所有值为"待删除"的项,新手通常会这样写:

d = {"a": 1, "b": "待删除", "c": 3} for key in d: if d[key] == "待删除": del d[key]

运行到一半,Python直接抛RuntimeError: dictionary changed size during iteration。原因很简单——字典在迭代过程中大小发生了变化,迭代器的内部状态乱了。

正确的做法是先把要删除的键收集到一个列表里,遍历完之后再统一删除:

to_delete = [] for key, value in d.items(): if value == "待删除": to_delete.append(key) for key in to_delete: del d[key]

或者用更加Pythonic的字典推导式重建一个新字典:

d = {key: value for key, value in d.items() if value != "待删除"}

第二种方式不修改原字典而是创建新字典,在数据量不大的时候完全够用,代码也干净。数据量特别大时再用第一种"先收集再删除"的方式,避免复制整个字典的内存开销。

4.4 可变对象做默认值:和字典关联的经典陷阱

这里虽然讲的是函数默认参数,但和字典的联系非常紧密。很多人在写函数时为了省事,这样写:

def add_student(name, course_scores={}): course_scores[name] = 90 return course_scores

第一次调用add_student("张三")返回{'张三': 90},第二次调用add_student("李四")你以为会返回{'李四': 90},结果返回的是{'张三': 90, '李四': 90}——因为默认参数{}在函数定义时创建了一次,之后所有调用共用同一个字典对象。

正确的写法是默认参数设为None,在函数内部再初始化:

def add_student(name, course_scores=None): if course_scores is None: course_scores = {} course_scores[name] = 90 return course_scores

这个坑看似和条件判断无关,但它是Python里和"可变对象"有关的最高频坑之一。第四天学字典时正好会碰到这种场景,提前记下来能省掉后面不少排查时间。

5. 综合小实战:写一个简易通讯录

5.1 需求拆解:把模糊想法变成明确功能

学编程最有成就感的一刻,就是用学到的东西做一个能"用起来"的小工具。我现在就带你一步一步做个最简单的通讯录,它要做的事情:

  • 用字典保存联系人,键是姓名,值是电话号码
  • 支持添加联系人
  • 支持按姓名查找电话
  • 显示所有联系人
  • 用条件判断处理"联系人不存在的场景"

先想清楚数据模型:contacts = {"张三": "13800138000", "李四": "13900139000"},非常简单明了。

5.2 完整代码与逐段解读

contacts = {} def add_contact(name, phone): if name in contacts: print(f"联系人 {name} 已存在,如需修改请联系人移除后再添加") return False contacts[name] = phone print(f"已添加联系人:{name}") return True def find_contact(name): phone = contacts.get(name) if phone is None: print(f"未找到联系人:{name}") return None print(f"{name} 的电话是:{phone}") return phone def list_contacts(): if not contacts: print("通讯录还是空的") return for name, phone in contacts.items(): print(f"{name}: {phone}") add_contact("张三", "13800138000") add_contact("李四", "13900139000") add_contact("张三", "13700137000") # 重复添加,会被拦截 find_contact("张三") find_contact("王五") # 不存在的联系人,不会崩溃 list_contacts()

这段代码把第四天学到的几乎所有知识点都用上了:

  • 字典存数据:contacts[name] = phone
  • 条件判断控制流程:if name in contacts检查重复
  • get()方法安全取值:phone = contacts.get(name)配合if phone is None判断
  • if not contacts判断空字典:利用空字典隐式转换为False的特性
  • items()遍历键值对

最值得体会的是find_contact函数的写法——它用了三层防护:get()代替直接索引避免KeyError、is None判断去除误判、返回None让调用方可以继续做后续处理,而不是让程序崩溃。这种"尽量不报错,给出明确反馈"的容错设计,是工程代码的重要思维习惯,和第2天学的"让程序走通"已经不是同一个层次了。

5.3 再进一步:用while循环让通讯录真正可以交互

如果想让程序像一个真正的工具,可以加上一个循环菜单:

while True: print("\n1. 添加联系人") print("2. 查找联系人") print("3. 查看所有联系人") print("4. 退出") choice = input("请输入操作编号:") if choice == "1": name = input("请输入姓名:") phone = input("请输入电话:") add_contact(name, phone) elif choice == "2": name = input("请输入要查找的姓名:") find_contact(name) elif choice == "3": list_contacts() elif choice == "4": print("已退出通讯录") break else: print("无效的操作编号,请重新输入")

注意这里的input()返回的是字符串,所以判断条件必须写成choice == "1"而不是choice == 1。这是一个很低级但非常高频的bug——从input()键盘输入的数字永远是字符串。如果没有这一层意识,你会发现明明输入了1,程序却永远进不去第一个分支。

这个小小的循环,加上之前的函数和字典,一个"能跑起来、能处理异常输入、能反复使用"的完整小程序就诞生了。第四天能把这种程度的代码自己写出来,说明前面三天的知识真正开始融会贯通了。

5.4 这个代码还能怎么扩展

如果你学有余力,可以自己动手加这几个功能,检验一下今天的内容是否真的吃透了:

  • 支持修改联系人电话:先判断联系人存在,再用新值覆盖旧值
  • 支持删除联系人:del contacts[name],但要先判断存在性,避免KeyError
  • 手机号格式简单校验:用len(phone) == 11这类条件判断做基础验证
  • 按姓名的首字母分类:如果联系人多,可以做一个"首字母 -> [联系人列表]"的嵌套字典结构

第5个扩展其实很有意思——{"Z": ["张三"], "L": ["李四"]}这样的嵌套结构,同时用到了字典的值是列表、条件判断处理首字母、以及setdefault方法。做出来之后,你对"复合数据结构"的理解会上一个台阶。

学Python第四天,我的体会是:条件判断和字典是代码从"玩具"走向"工具"的分水岭。前者让程序有了逻辑,后者让程序有了组织数据的能力。把这两者练熟,接下来学函数、文件操作、面向对象,都会顺滑很多。尤其是字典,等你以后接触真实项目,从配置管理、接口返回的JSON解析,到爬虫数据存储,几乎每一处都能见到它的身影。今天把字典的常用操作和条件判断的组合方式练扎实,后面就是复利效应。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 17:28:53

Modbus协议取证实战:从流量分析到内存排查的完整路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 17:28:48

SpringBoot网络流量样本管理系统设计与实践

1. 项目概述与背景网络流量数据样本管理是网络安全分析、业务监控和性能优化等领域的基础工作。这个基于SpringBoot的JavaWeb系统,专门用于对网络流量样本进行采集、存储、分析和可视化展示。我在实际企业级安全项目中,发现传统文件系统管理流量样本存在…

作者头像 李华
网站建设 2026/9/18 17:28:47

faster-whisper 离线语音转写快速上手

faster-whisper 离线语音转写快速上手 【免费下载链接】faster-whisper Faster Whisper transcription with CTranslate2 项目地址: https://gitcode.com/GitHub_Trending/fa/faster-whisper 如果 40 分钟的会议录音跑十几分钟才出稿、内存几乎被打满,这就是…

作者头像 李华
网站建设 2026/9/18 17:28:14

Java Swing多线程实现无人机防空仿真系统

1. 项目概述这个无人机自动防空平台项目是一个基于Java Swing和多线程技术的仿真系统。作为一名有多年Java开发经验的程序员,我发现这个项目很好地结合了GUI编程和并发编程的核心知识点。系统模拟了无人机防御场景,包含雷达扫描、敌机追踪等基础功能模块…

作者头像 李华