资讯动态

Python分支结构入门:if、elif与真假判断核心规则

发布时间:2026/10/21 10:09:19 来源:尧图企业网站定制
每一个学编程的人都会碰到这个问题程序一条一条顺序往下跑只会“直线前进”遇到需要判断的情况就傻了。无论是做一个简单的登录校验还是写一个自动打卡的小工具都绕不开“如果……就……”这种逻辑。分支结构就是程序“长脑子”的第一步——它让代码有了选择的能力不再是一根筋地执行到底。这篇是“Python快速闯关”系列的第三关。前面两关我们搞定了变量、基本数据类型的运算、以及程序的顺序执行。这一关我要把分支结构讲透从最基础的if语句怎么写到多分支怎么编排才不踩坑再到Python里那些特别容易让初学者翻车的真假判断规则最后用几道带参考解法的闯关题收尾。适合刚学完变量和数据类型、准备进入控制流部分的同学也适合学了if但又总出错的老手查漏补缺。1. 为什么程序需要“长脑子”从顺序到选择的转变1.1 结构化编程的三驾马车先退一步看一个大局不管多复杂的程序它的控制流程其实只有三种基本结构——顺序、分支、循环。顺序结构就是从上往下逐行执行这个你已经在前两关见过了没什么好说的。循环我们下一关再聊这一关的主角是分支结构。分支结构解决的是“选择”问题程序运行到这里面前有两条或更多的路到底走哪条取决于某个条件是真是假。你平时接触到的所有交互逻辑——登录时判断密码对不对、下单时判断库存够不够、游戏里判断角色有没有碰到障碍物——底层都是分支结构在驱动。理解分支结构本质上就是理解“程序如何在不同的数据状态下做出不同反应”。从代码组织的角度看分支结构也决定了代码的“形状”。顺序结构的代码像一条直线而分支结构的代码会产生“分叉”分叉之间互斥最后又重新汇合。这种“分叉再汇合”的模式是整个程序逻辑复杂度提升的起点。1.2 最小if结构第一个条件分支Python里最基础的分支语句是if。它的语法结构长这样if 条件: 条件成立时要执行的代码这里有几个至关重要的细节初学者几乎都会栽跟头第一条件后面必须有一个冒号。这个冒号是Python语法的一部分表示“下面是一个代码块”。漏掉冒号会直接报语法错误。第二条件下面的代码必须缩进。Python用缩进而不是花括号来划分代码块这是它跟C、Java最直观的区别。条件成立时要执行的语句必须比if本身多一个缩进层级通常是四个空格。第三缩进必须一致。同一个代码块里的多行语句缩进的空格数必须相同不能用两个空格开头然后又用四个空格这会触发IndentationError。看一个最小可运行的例子score 86 if score 60: print(及格了) print(继续加油) print(这句不管怎样都会执行)运行结果是及格了 继续加油 这句不管怎样都会执行你要注意最后一行print(这句不管怎样都会执行)和if是对齐的所以它不属于if的代码块。不管条件真假它都会执行。这里顺便说一个很常见的误解很多人以为if后面的条件必须是一个“比较运算”比如score 60。其实不然条件可以是任意表达式只要这个表达式的值能被解释成“真”或“假”即可。哪怕你直接写if 1:它也是合法的因为1在Python里被看作真值。关于这个“真假判断”的细节我们到第4章专门展开它是Python分支结构里最容易让人困惑的部分。1.3 代码块与缩进别小看这四个空格既然缩进在Python里是语法的一部分那就有必要多说两句。我见过不少从别的语言转过来的朋友写Python时还是习惯性地打完if条件之后按一下Tab甚至随手敲几个空格结果要么报缩进错误要么因为缩进不一致被IDE标红。说实话Python的缩进规则虽然严但恰恰因为严它强制你把代码块看成一个“整体”这对培养良好的代码结构感是有帮助的。现在绝大多数编辑器比如VS Code、PyCharm都默认把Tab键转换成四个空格你只要保持设置默认基本上不会遇到Tab和空格混用的问题。真正容易出问题的是复制粘贴——从网页或者聊天工具里复制一段代码缩进会悄悄变成全角空格或者混合空白一运行就报错。提示如果你遇到TabError: inconsistent use of tabs and spaces in indentation最快的解决办法是在编辑器里选中这段代码执行“将缩进转换为空格”然后重新调整缩进。还有一点值得注意空行和注释不影响代码块的结构。if分支下面如果有很多内容你为了可读性中间插入空行这个空行并不会把代码块截断因为Python靠的是缩进级别而不是空行来划分块。2. else与elif二选一与多路选择2.1 else不是必选项但往往是加分项单独的if只能表达“成立时做什么”如果条件不成立程序什么都不做直接跳过。但在大量业务场景里我们需要的是“二选一”条件成立走一条路不成立走另一条路。这时候就要用else。score 47 if score 60: print(及格了) else: print(不及格需要补考)输出不及格需要补考else不需要写条件它跟if配套使用表示“前面那个条件不成立时执行这里的代码”。它的本质是if条件的取反但要注意这里的“取反”不是数学上的严格取反而是逻辑上的相反分支。也就是说只要不进入if分支就会进入else分支。这个看起来人畜无害的设计有一个容易被忽略的坑如果if条件写得不够严谨那么else分支会包含所有“不满足条件”的情况其中可能包括你根本没考虑到的意外值。比如grade B if grade A: print(优秀) else: print(其他等级)这里如果grade是C走else没问题但如果grade是Z甚至是一个None它也走else。这类非黑即白的双分支在有更多等级时就会失真所以才有下一节的elif。2.2 elif多分支的正确打开方式编程里的选择题往往不止两个选项。成绩要分“优秀/良好/及格/不及格”会员要分“普通/银卡/金卡/黑卡”这时候if...else写起来就会变成嵌套的“俄罗斯套娃”非常痛苦。Python 提供了elif它是“else if”的缩写用来连接多个互斥分支。score 78 if score 90: print(优秀) elif score 75: print(良好) elif score 60: print(及格) else: print(不及格)输出良好很多人第一次看到这段代码的疑问是“为什么不直接写成三个独立的if” 答案是效率与语义的双重考虑。从语义上讲elif表达的是“否则如果”——只有前面的条件都不成立才会检查这一个条件。一旦某个elif成立后面的elif和else统统跳过不再判断。而三个独立的if则是完全独立的判断每个都会执行。从效率上讲elif链是短路判断的命中前面的分支后面就不用比了独立if则要全部比完。数据量小的时候差异可以忽略但写程序一开始就要养成该互斥就互斥的好习惯。2.3 分支顺序是敏感话题差之毫厘谬以千里elif链里分支的书写顺序极其重要。上面的分数例子如果一个粗心的同学把条件写成了这样score 88 if score 60: print(及格) elif score 75: print(良好) elif score 90: print(优秀)你会发现score 88时输出的是“及格”而不是“良好”。原因很简单第一个条件score 60已经成立了程序直接进入这个分支后面的elif根本不会被执行。这就是elif链的核心规则从上到下依次判断遇到第一个成立的条件就停下。所以条件的排列必须遵循“范围从窄到宽”或“优先级从高到低”的原则把更特殊的条件放前面更宽泛的条件放后面。再举一个游戏里常见的例子判断角色等级并分配称号level 99 if level 100: title 传说级 elif level 60: title 高手级 else: title 新手级假如level 100输出是“传说级”。把条件顺序反过来写成先判断level 60那任何大于等于60的玩家都会拿到“高手级”称号永远没有机会进入“传说级”。我在带初学者的时候有一条建议反复强调写多分支时先画一张表把条件和对应结果列出来再对照着写代码。别看这个动作简单它能让分支顺序出错率直线下降。3. 条件表达式比较、逻辑运算符与优先级3.1 比较运算不是只有“等于”分支里放得最多的就是比较表达式。Python的比较运算符一共有这些大于、小于、大于等于、小于等于、等于、!不等于。它们都返回布尔值True或False。这里要敲黑板是判断相等是赋值。初学者特别容易把这两个搞混写条件时不知不觉少写一个等于号变成赋值表达式结果条件永远为真。比如x 10 if x 5: # 这只是给 x 赋值为5不是判断 print(不会报错但逻辑完全错了)这种代码在Python里不会直接报语法错误因为赋值表达式在大多数上下文中是合法的在某些版本中甚至会因为赋值表达式规则不同产生奇怪表现但它的行为绝对不是你想的比较。如果你发现if分支里明明条件不成立却总是执行第一反应去查等号是不是写成了赋值。还有一个新手很少注意的点Python支持链式比较这跟数学里的写法很贴近score 85 if 60 score 90: print(中等偏上)这段代码等价于score 60 and score 90。链式比较不仅可读性更好而且在Python内部只对中间变量求值一次逻辑上更加紧凑。我在写区间判断时几乎总是用它强烈推荐你也养成这个习惯。3.2 逻辑运算and / or / not现实中很少只有一个条件的分支。比如“登录成功”要同时满足“用户名存在”且“密码正确”“周末去玩”的条件是“非工作日”或“天气好”。Python用and、or、not三个逻辑运算符来组合多个条件。a and ba 和 b 都为真时结果才为真。a or ba 和 b 至少一个为真结果就为真。not a对 a 取反。看一个综合案例age 25 is_member True if age 18 and is_member: print(可以进入会员专享区) else: print(不符合条件)and两侧的条件可以是比较表达式也可以是其他任意表达式只要它们的真值能被解释。后面第4章会看到这一点非常灵活。逻辑运算符有优先级规则not最高and其次or最低。也就是说a or b and c等价于a or (b and c)而不是(a or b) and c。记不住优先级也没关系在复杂的组合条件里加括号是最稳妥的做法。括号不但明确语义还能让看代码的人不用想就知道你要表达什么。我在实际项目里几乎不给or和and混合的表达式留“裸奔”的机会统一加括号这是职业习惯也是保护罩。3.3 短路求值Python 逻辑运算符的另一面and和or有一个非常实用的特性叫短路求值short-circuit evaluation。对a and b来说如果a已经是假那么不管b是什么整个表达式的值都必然是假。Python不会再去计算b因为它没必要。对a or b来说如果a已经是真那么不管b是什么整个表达式的值都必然是真Python同样会跳过b。这个特性有两个重要影响。第一它可以避免一些可能出错的运算。比如判断一个数字是否能被另一个数整除但要防止除数为零a 8 b 0 if b ! 0 and a % b 0: print(可以整除) else: print(不能整除或除数非法)如果b 0第一个条件b ! 0为假短路生效Python直接跳过a % b 0根本不会执行除法取余运算也就不会产生ZeroDivisionError。如果把条件调换顺序写成a % b 0 and b ! 0那么a % 0会先执行直接抛异常——这就是短路顺序的价值。第二and和or并不总是返回布尔值。事实上Python的逻辑运算返回的是参与运算的操作数之一而不是简单返回True或False。具体规则是a and b如果a为假返回a否则返回b。a or b如果a为真返回a否则返回b。这个特性在日常代码中可以用来做默认值设定。比如nickname input(请输入昵称).strip() display_name nickname or 匿名用户当nickname是空字符串时它的真值为假这个细节在第4章会细讲or表达式就会把nickname or 匿名用户的结果赋给display_name于是用户没填昵称时自动显示“匿名用户”。这个写法简洁又有表现力比写一个完整的if...else更省事。但我要提醒一句这种“利用返回值做赋值”的写法适合表达清晰的惯用法。如果逻辑太绕别人看不懂宁可老老实实写if...else可读性永远比炫技重要。4. Python的真值观与隐式布尔新手最常栽跟头的地方4.1 空即假有即真C语言里整数0表示假非0表示真Python继承并扩展了这个思路但规则更加统一。在Python中以下值在布尔上下文中被视为假False布尔值False数字0、0.0、0j空字符串空列表[]、空元组()、空字典{}、空集合set()特殊值None其他所有值——非零数字、非空字符串、非空容器——在布尔上下文中都被视为真True。这个规则带来一个非常实用的习惯判断一个列表是否为空时不需要写if len(list) 0直接写if list:就行。tasks [] if tasks: print(还有待办任务) else: print(所有任务已完成)输出所有任务已完成很多从C或Java转过来的朋友会觉得这种写法“太隐晦”担心可读性下降。但说实话在Python社区里if tasks:是一种公认的惯用法Python老手一眼就能看懂。你不必强迫自己立刻习惯它但至少要认识它否则看到别人的代码会一头雾水。反过来这个规则也意味着一个隐藏陷阱如果一个变量的值可能是0、空字符串时你在if里直接判断它会被误判为“假”。比如discount 0 # 折扣力度为0表示没有折扣 if discount: print(有折扣) else: print(无折扣)输出是“无折扣”这没问题。但假如业务里折扣为0也是一种有效状态而你想表达“有折扣”并计算折扣后的价格这么写就会跳过0这个有效值。这种场景下应该用if discount is not None:显式判断让“没有折扣”和“折扣为0”区分开。4.2 None的判断你该用is而不是None是Python里一个特殊的值表示“什么都没有”或者“尚未赋值”。它最常见的用途是作为可选参数的默认值以及表示缺失的数据。判断一个变量是否为None规范的写法是用身份运算符isuser None if user is None: print(用户未登录) else: print(用户已登录)为什么不推荐用去判断None因为比较的是值相等而None在绝大多数情况下也确实只有它自己一个值所以x None在功能上基本等价于x is None。但问题在于会触发对象的重载比较逻辑某些自定义类型可能把__eq__方法实现得乱七八糟导致x None返回意外结果。而is比较的是对象的身份也就是“是不是同一个对象”它不会被__eq__干扰判断更可靠、更明确。所以Python官方风格指南PEP 8也建议与None的比较应该用is或is not不要用。4.3 与 is相等与身份的差别顺着上一节我们把和is的区别一次性说清楚。这是Python初学阶段高频出现的一个混淆点。比较的是两个对象的“值”是否相等。比如1 1.0结果是True因为数值相等。is比较的是两个对象是否是“同一个对象”内存地址是否相同。1 is 1.0结果是False因为整数1和浮点数1.0在内存中是两个不同的对象。看一个经典例子a [1, 2, 3] b [1, 2, 3] c a print(a b) # True值相同 print(a is b) # False两个不同的列表对象 print(a is c) # Truec就是a本身在分支判断里如果你只是想判断“内容是否相等”用如果你想判断“是否是同一个对象”用is。大部分场景我们用就够了只有判断None、以及判断对象的单例状态时才需要is。这里还要插入一个Python特有的小陷阱。小整数和短字符串在Python里有缓存机制所以在某些时候你会看到a 256 b 256 print(a is b) # True因为小整数被缓存指向同一个对象但接着试a 257 b 257 print(a is b) # 可能是 False超出缓存范围各是各的这种结果在不同Python实现和版本下可能不一样所以永远不要依赖is来判断数字或字符串是否相等。看到有人这么写十有八九是从别的语言留下的坏习惯建议直接改成。4.4 条件表达式的“地雷”把赋值放进if回到第3.1节提到的if x 5我在这里再展开讲一下因为这个问题出现的频率实在太高了。写过C语言的人都知道C的赋值表达式可以作为条件的一部分if (x 5)会被编译且因为x被赋值为5条件恒真。很多从C转Python的人会不自觉地把这个习惯带过来。但在Python里if x 5:绝大多数场景下会直接报语法错误因为Python不允许在条件表达式里做赋值除非用海象运算符:那是另一回事。如果真的写出来了错误提示大概长这样SyntaxError: invalid syntax而如果写成if x 5:这才正确。如果你的程序出现疑似逻辑错误但不报错、分支又总是不按预期走检查比较运算符是排在第一位的基本操作。5. 分支嵌套与写法简化从“套娃”到“清爽”5.1 嵌套分支与缩进地狱分支里套分支是写业务逻辑逃不开的场景。比如判断一个用户的权限is_login True is_vip False if is_login: if is_vip: print(欢迎VIP用户) else: print(欢迎普通用户) else: print(请先登录)两层嵌套还看得过去但一旦嵌套到三四层代码就成了“箭头旋转90度”的形状——每层多一层缩进阅读体验断崖式下降这在业界有个著名的外号叫“箭头反模式”或“缩进地狱”。嵌套过深的最大风险不是“丑”而是容易把逻辑关系弄乱。你很难一眼看出来某个else到底跟哪个if配对。虽然Python的缩进规则保证了解析的唯一性但人脑不是解析器看错是常有的事。5.2 卫语句把异常情况提前挡在门外对付嵌套地狱最有效的手段之一是卫语句guard clause也叫提前返回。核心思路是把不满足前置条件的情况先行处理掉用ifreturn或者continue、break取决于代码上下文直接结束当前流程这样主干逻辑就不用被一层层嵌套包围。举一个没有用卫语句的例子def process_order(order): if order is not None: if order.status paid: if order.stock 0: print(开始打包发货) else: print(库存不足无法发货) else: print(订单未支付) else: print(订单不存在)这个三层嵌套虽然逻辑没错但读起来很费劲。用卫语句重写之后def process_order(order): if order is None: print(订单不存在) return if order.status ! paid: print(订单未支付) return if order.stock 0: print(库存不足无法发货) return print(开始打包发货)对比一下重写后的代码每一层条件都“拦截”掉一个异常情况主干路径一路到底非常清晰。这个技巧不仅适用于函数内部的return也适用于循环里的continue。我在实际写代码时几乎默认使用卫语句来整理复杂逻辑它治好了我的“嵌套恐惧症”。5.3 用字典替代多重if条件与数据的解耦还有一种常见的多分支场景不是按范围分而是按枚举值分。比如根据套餐类型获取名称plan_code PRO if plan_code FREE: plan_name 免费版 elif plan_code BASIC: plan_name 基础版 elif plan_code PRO: plan_name 专业版 elif plan_code ENTERPRISE: plan_name 企业版 else: plan_name 未知套餐写这么一串elif实在冗余。这种“键值映射”型的分支用字典是更Pythonic的解法plan_map { FREE: 免费版, BASIC: 基础版, PRO: 专业版, ENTERPRISE: 企业版, } plan_code PRO plan_name plan_map.get(plan_code, 未知套餐)dict.get(key, default)这个方法本身就有“如果键不存在就返回默认值”的语义正好对应了else分支。这一行代码把上面6行if...elif干掉的活全干完了。当分支分支数量达到四五个以上只要映射关系是明确的常量对应优先考虑字典法。对于有明确数学或逻辑规则的多分支比如分数区间字典法不适用仍要回到if或elif。判断用哪种就看条件之间是“区间规则”还是“离散映射”。5.4 match-casePython 3.10带来的新选择如果你的Python版本在3.10及以上还有一个结构性模式匹配语法match可以用。上面的套餐例子用match来写plan_code PRO match plan_code: case FREE: plan_name 免费版 case BASIC: plan_name 基础版 case PRO: plan_name 专业版 case ENTERPRISE: plan_name 企业版 case _: plan_name 未知套餐match的case _相当于默认分支类似else。这个语法在匹配复杂结构元组、字典、类实例时威力很大但如果你只做简单的等值判断match和字典映射哪个更好用全看个人喜好。我的建议是先掌握好if...elif...else和字典法这两种已经能解决绝大多数问题。match作为一个加分项等基础扎实了再学也不迟。6. 三道闯关题从复刻到变式再到实战6.1 第一关闰年判断闰年的规则是能被4整除但不能被100整除的年份是闰年能被400整除的年份也是闰年。其他年份不是闰年。要求输入一个年份输出它是“闰年”还是“平年”。参考思路这道题考的是多条件组合与逻辑运算符。需要注意“能整除”要用取模运算%。year int(input(请输入年份)) if year % 400 0 or (year % 4 0 and year % 100 ! 0): print(f{year}年是闰年) else: print(f{year}年是平年)对小项目来说year % 400 0放第一个条件利用短路特性优先命中400倍数这种特例逻辑清晰简洁。要注意input()返回的是字符串直接%运算会报错必须先用int()转换。6.2 第二关BMI 指数分级BMI 体重(kg) / 身高(m) 的平方。分级标准如下低于18.5为“偏瘦”18.5到24.9为“正常”25到29.9为“偏胖”30及以上为“肥胖”。要求输入体重和身高输出对应的BMI值和等级。参考思路这道题考的是多分支顺序。因为区间从低到高排列是自然的所以可以从小到大依次判断每个分支进一步细分。weight float(input(请输入体重(kg))) height float(input(请输入身高(m))) bmi weight / (height ** 2) print(f你的BMI指数为{bmi:.1f}) if bmi 18.5: level 偏瘦 elif bmi 25: level 正常 elif bmi 30: level 偏胖 else: level 肥胖 print(f你的体质等级{level})这里一个关键技巧是因为前面的条件已经把 18.5排除了到达elif bmi 25时实际上隐含了bmi 18.5的前提。所以写分支时不用重复写完整区间只写“上边界”即可。这是elif顺序敏感性的正向利用。把分支顺序反过来写结果就会错得离谱——你可以自己试试。顺带提示体重和身高都要用float()转换否则字符串无法参与除法。如果输入了非数字内容程序会直接抛ValueError这里不做异常处理等后面学try...except再来处理。6.3 第三关剪刀石头布判定要求玩家和电脑各出一个手势机制作剪刀、石头、布判定谁赢。参考思路这道题考的是多组合条件的逻辑编排。可以先把玩家和电脑出的手势转换成统一的编号石头0剪刀1布2然后用取模运算来判定胜负也可以用最直白的and组合枚举所有胜利情况。这里给一个用组合条件的版本import random player input(请输入你的手势剪刀/石头/布) weapons [剪刀, 石头, 布] computer random.choice(weapons) print(f你出的是{player}电脑出的是{computer}) if player computer: result 平局 elif (player 石头 and computer 剪刀) or \ (player 剪刀 and computer 布) or \ (player 布 and computer 石头): result 你赢了 else: result 你输了 print(result)这段代码里的反斜杠\是行连续符表示下一行逻辑上还是同一行。也可以用括号把整个条件包起来这样更优雅if player computer: result 平局 elif ( (player 石头 and computer 剪刀) or (player 剪刀 and computer 布) or (player 布 and computer 石头) ): result 你赢了 else: result 你输了最后这个版本的可读性明显更好。这里要特别注意or的优先级比and低但加了括号之后完全不需要担心。如果你想更严谨还应该检查player是否在weapons列表中不在的话提示输入格式错误这又用到下一关要学的循环了。6.4 调试分支代码的个人经验最后分享一点调试心得。分支结构刚上手时最容易出现的情况不是“不会写”而是“写完不知道哪里错”。我推荐一个最原始但也最有效的调试方法在关键分支里临时加打印语句把进入每个分支时涉及的变量值打出来。score 59 print(调试score , score) if score 60: print(进入及格分支) print(及格) else: print(进入不及格分支) print(不及格)运行后你能清清楚楚看到程序走进了哪个分支、当时变量是什么。排查完之后再把调试用的打印删掉。另一个笨但稳妥的习惯是在写多分支之前先列出条件表格。拿成绩分级来说区间等级条件优先级 90优秀先判断75 ~ 89良好次判断60 ~ 74及格再判断 60不及格兜底然后照着表写elif保持条件从小范围到大范围基本不会出错。这个习惯一度让我少踩了很多坑现在每次写复杂分支我也还会在草稿上画这个表。分支结构是整个流程控制里的第一道坎内容本身不多但牵扯的细节不少。把这些细节吃透尤其是真值判断和elif的顺序敏感问题你后面写循环、写函数、写业务逻辑时都会顺畅得多。这一关的内容可以到这里结束消化题目之后就可以进入下一关——循环结构了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑