ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python条件控制语句全面解析:从真假值到match-case实战

Python条件控制语句全面解析:从真假值到match-case实战 说实话写了这么多年Python我越来越觉得一件事条件控制语句看似是入门第一课但绝大多数线上bug和逻辑混乱最后都能追溯到if、elif、else这些不起眼的关键字上。很多朋友学Python一上来就奔着爬虫、量化、Web框架去条件判断随便看看就跳过结果真到写业务逻辑的时候不是缩进乱了就是真假值判断翻车或者elif顺序排错导致永远走不到想要的逻辑分支。今天这帖子我就把条件控制语句这块彻底掰开揉碎讲一遍从真假值、缩进、边界条件到三元表达式、match-case匹配再到实际项目里的组织方式和排查手段全部过一遍。适合刚入门想打牢基础的人也适合写了两三年代码但偶尔被if搞懵的老手。1. Python条件判断的底层逻辑与真假值1.1 为什么Python的if长得和C、Java不一样先聊个很多新手第一次见到Python的if时都会愣一下的点判断条件不需要加括号。if x 0: print(正数)C系语言里大概是if (x 0) { printf(正数); }Python去掉括号、去掉花括号改用冒号和缩进来划定代码块。这个设计让代码看起来非常干净阅读理解成本低但也带来了一个很多人吐槽的问题缩进本身就是语法的一部分错了就运行不了甚至运行出完全不符合预期的结果。if x 0: print(正数) print(还是正数) # 这个也在if块里 print(不管怎样都会执行) # 这个不在if块里很多人写代码时想省事随手打个Tab或者混用空格和Tab直接报IndentationError。说实话Python官方明确要求统一用四个空格但实际开发里不少人用Tab也用了好多年关键不是用哪个而是同一份代码里必须统一千万别混用。我个人的习惯是代码编辑器一律把Tab自动转为四个空格保存时自动清理行尾空格和末尾空行。这看起来是小事但在团队协作时能少很多无意义的diff冲突。1.2 Python内存中有一张“真假值清单”Python的if判断背后其实不是简单的true和false两个布尔值。任何对象放到if的条件位置时Python都会自动调用该对象的__bool__()方法如果没定义这个方法就调用__len__()如果两个都没有那这个对象默认就被当作真值处理。这意味着以下这些值放在if条件里一律判断为假False None 0 0.0 空字符串 []空列表 ()空元组 {}空字典 set()空集合除此之外其余所有对象都是真值。很多人以为if []会报错实际上它不会报错而是直接走else分支。这个机制用好了代码会非常简洁。比方说判断一个列表是否为空新手可能写成if len(items) 0: do_something()老手一般直接写if items: do_something()因为在if语句中空列表会被自动判定为假非空列表判定为真。同理判断字符串是否非空、字典是否有键值对都可以直接用对象本身作为条件不需要额外比较长度。这个习惯不仅写起来顺手读代码的人也不会觉得有负担。不过这里也得提醒一句真值判断不适用于所有场景。比如判断一个变量是否为None时不要写if not x因为当x等于0、空字符串、空列表时if not x也会成立。这个时候应该明确写if x is None。这两个写法在语义上是完全不同的。1.3 布尔运算背后的短路机制与返回值Python的and和or是很多教程一句带过、但实际用起来非常容易踩坑的知识点。它们并不总是返回布尔值而是返回参与运算的对象本身。result None or default print(result) # default result2 value and 42 print(result2) # 42or的规则是从左往右找第一个真值找到就返回它如果全是假值返回最后一个假值。and的规则是从左往右找第一个假值找到就返回它如果全是真值返回最后一个真值。这个特性在实战中非常有用。比如从配置中取值如果取不到就使用默认值name config.get(name) or 游客如果config.get(name)返回了空字符串或Noneor就会继续往后走把游客作为最终结果。再比如用在函数参数的默认处理上逻辑非常直白。与之配套的还有短路机制。and左侧为假时右侧根本不会执行or左侧为真时右侧也不会执行。这个特性可以用来安全地写一些原本需要if判断的逻辑user and send_email(user.email)如果user为Nonesend_email就不会被调用也就不用先if判断再调用了。当然这种写法在项目里要克制使用因为可读性对部分同事来说不一定友好但在一些工具脚本、配置解析场景里确实能写得特别干净。2. if-elif-else分支设计与边界条件2.1 elif不是elseif它是互斥逻辑的关键很多入门教程讲if-elif-else时只会强调“多个条件按顺序匹配匹配到就执行后面的不看了”。但实际写业务代码时条件分支的效率与正确性跟条件的排列顺序关系极大。先看一个容易出问题的例子score 85 if score 60: print(及格) elif score 80: print(优秀)这个代码执行后输出的是“及格”而不是“优秀”。因为第一个条件score 60已经满足了Python不会再往下看elif score 80。这其实是互斥分支的核心特征一旦某个条件满足后续所有条件都被跳过。所以在设计多条elif的时候一定要养成“从严到松”的顺序习惯。比如判断成绩等级正确的顺序是先判断最高标准再逐级往下if score 90: grade 优 elif score 80: grade 良 elif score 60: grade 及格 else: grade 不及格这个顺序看起来理所应当但我见过不少新手把顺序反过来写导致“良”“优”分支永远执行不到。排查这种bug时其实只要把条件值代入逐行走一遍马上就露馅了。2.2 条件顺序影响性能的实测原理条件顺序除了影响正确性还会影响性能。Python解释器对if-elif结构是逐条判断、逐个执行比较操作的遇到第一个满足的条件就停止。所以理论上把命中概率高的条件放在前面可以减少平均判断次数。举个实际场景。接口校验用户权限时需要区分匿名用户、普通用户、VIP用户、管理员这几类。如果匿名用户占比最高就应该把匿名判断放在最前面if user is None: handle_anonymous() elif user.vip_level 0: handle_vip() elif user.is_admin: handle_admin() else: handle_normal()当然这个优化在绝大多数业务场景里属于“锦上添花”判断一次条件也就微秒级开销除非在超大规模循环里否则感知不明显。但养成把“高概率条件放前面”的排列习惯本身也是在帮自己理清业务优先级。2.3 空分支用pass但别滥用有时候某个条件下什么也不做比如异常处理里只记录日志不处理或者预留逻辑位置。此时可以用pass来占位让语法结构保持完整if debug_mode: pass else: run_normal()这个写法是合法的但我在实际项目中会尽量避免空分支。如果一个条件分支什么都不做那通常意味着这个条件本身就是多余的或者逻辑设计上可以考虑合并。pass不是不能用但出现在代码评审里总会引发“这里是不是还没写完”的疑问。2.4 嵌套if与条件合并的取舍嵌套if是新手最容易写出“屎山”的地方。if age 18: if has_ticket: if not banned: print(允许入场)这种三层嵌套读起来还算能忍但如果嵌套到五层以上代码就基本没法维护了。解决的思路有两个一是把多个条件用and合并到同一层判断二是把内层判断拆成独立的函数用“提前返回”替代嵌套。if age 18 and has_ticket and not banned: print(允许入场)如果条件之间是互不相关的业务规则第二种“提前返回”的写法更推荐def can_enter(person): if person.age 18: return False if not person.has_ticket: return False if person.banned: return False return True预告一下这种“早退式”写法在真实项目里对可读性的提升非常明显后面第5节我还会专门展开讲。3. 条件语句里的核心语法细节与常见搞混点3.1 在同一行写简单判断不建议但你要能看懂有时候在网上刷代码会看到这种写法if x 0: print(正数)这是合法的冒号后面跟一条简单语句即可。但我不建议在项目里这么写。原因很简单当你要往这个分支里追加第二行时很容易忘记缩进问题更重要的是在代码评审和diff对比时单行if很难一眼看出分支范围。我曾经接手过一个项目里面大量使用单行if改起来极度痛苦——你想调整一个分支的代码第一反应是“这行到底管到哪”3.2 条件表达式的写法带括号是个好习惯虽然Python的if不需要括号但当一个条件里同时包含多个逻辑运算符时可读性和可维护性就必须主动考虑了。看这个表达式if a or b and c:and的优先级高于or所以它实际等价于a or (b and c)。但如果你没记住优先级或者同事没记住这段代码就会产生理解的鸿沟。更稳妥的写法是显式加括号if a or (b and c):之前维护过一个订单状态判断的老代码里面有一个特别长的复合条件大概是if order.status paid or order.status refunding and order.amount 100:每个接手的人都要先查运算符优先级才能理解逻辑。后来我把它改成了两个变量先算出来再合到一起用代码瞬间就清晰了is_paid order.status paid is_partial_refund order.status refunding and order.amount 100 if is_paid or is_partial_refund:3.3 is、与真假值三种判断别混用判断条件里最容易翻车的一个点就是is、和真假值混用。它们的语义完全不同。比较的是值只要两个对象的值相等就为真is比较的是身份只有两个变量指向内存中的同一个对象才为真真假值判断看的是对象本身在布尔上下文中是True还是False经典的案例是a 257 b 257 a b # True a is b # False因为257不是小整数常量两个对象分配在不同的内存地址对于-5到256的小整数Python有缓存机制is可能会返回True但这属于解释器的实现细节不该被依赖。日常代码里判断None只能用is None判断其他类型建议优先用。还有一个高频坑是if x None这种写法PEP 8明确建议改用if x is None因为None是单例对象is比更严谨、也更快。3.4 条件中直接给变量赋值新手最容易犯的错if后面的条件位置理论上可以放任何表达式但有一个经典错误经常出现在新手代码里if x 10: print(成立)这是非法的会直接报SyntaxError。原因是为了防止把写成这种在C语言里特别常见的坑。如果你在条件里确实需要先赋值再判断可以把赋值移到if之前或者用海象运算符:在表达式内赋值if (n : len(items)) 10: print(f数量为{n})海象运算符是Python 3.8引入的理解它的作用是“赋值表达式”——一边赋值一边参与判断。但我要提醒一句这个特性虽然好用但可读性争议很大。太多人把:用在各种不必要的地方结果代码变得很难读。我的原则是只有当它能让逻辑明显更紧凑时才用否则老老实实把赋值写在上一行。4. 三元表达式与match-case结构匹配4.1 三元表达式怎么写才不让人骂Python的三元表达式语法是x if condition else y这在很多语言里写作condition ? x : y。它的作用是做一个简单的二选一赋值。status 成年 if age 18 else 未成年这种写法适合简单场景一旦条件或两侧的表达式变复杂可读性就断崖式下跌。我见过一段代码result (x * 2 if flag else x * 3) (y 1 if y 0 else 0)这行代码的意图其实还行但读起来真的很费劲。如果你发现三元表达式超过一行或者需要加括号才能让读者理解那就应该拆成if-else块。给我的实际建议是三元表达式只用于“两个短表达式之间的二选一”超过这个规格就老老实实写if-else。写代码要照顾的人不是机器是六个月后的自己。4.2 Python 3.10的match-case模式匹配入门Python 3.10之后match-case语法正式进入语言。很多人看到match就说“这不就是switch-case嘛”其实它的能力比switch-case强不少。基础用法如下status_code 404 match status_code: case 200: print(OK) case 404: print(Not Found) case _: print(Unknown)case _相当于默认分支匹配所有情况。match-case还支持把匹配值绑定到变量上这就是所谓的“模式匹配”。比如按类型或按结构分发def process(data): match data: case 0: print(收到0) case int(val): print(f整数: {val}) case str(val): print(f字符串: {val}) case [first, *rest]: print(f列表首元素{first}其余{rest}) case {name: name}: print(f字典名字是{name}) case _: print(未知类型)4.3 结构匹配里几个容易忽略的细节第一个细节match的值匹配本质上是按语义去比较的所以自定义对象需要实现__eq__才能正确匹配。像是case 0不会因为0的布尔值是False而匹配到别的值match不会做真假值转换这一点和if完全不同。第二个细节是“通配符”case _的用途。_在模式匹配里不会被绑定成变量它只是占位。如果你写case name那任何数据都会匹配成功同时把值绑定到name上。很多人第一次用match的时候把_理解成“任意变量”实际上它确实是但不会被保留。第三个细节也是很多人忽略的在match-case中如果前面某个case已经匹配成功后面的case就不会再执行这个和if-elif的互斥逻辑相同但更严格。还有一个“or模式”可以用竖线在一个case里匹配多个值比如case 200 | 201: print(成功)这样可以把同类业务状态合并处理代码会清爽不少。不过使用match-case前也要注意一点如果你的Python版本低于3.10这个语法是跑不起来的。生产环境升级Python版本往往不是小事所以要不要用match-case取决于项目实际运行环境。5. 实战案例从需求到条件语句的完整设计5.1 案例一订单状态机的条件分支搭建假设现在要做一个订单状态处理模块订单状态有草稿、已支付、已发货、已完成、已取消五种。不同的状态允许执行的操作不一样。用if-elif可以快速写出一版if status draft: action 编辑 elif status paid: action 发货 elif status shipped: action 确认收货 elif status completed: action 查看评价 elif status cancelled: action 删除 else: action 未知操作这个写法能跑但有个问题如果状态多了比如又加了“退款中”“已退款”每个分支都是独立的业务规则代码会越来越长。更好的做法是用字典做映射action_map { draft: 编辑, paid: 发货, shipped: 确认收货, completed: 查看评价, cancelled: 删除, } action action_map.get(status, 未知操作)字典映射的本质是把“数据”和“判断逻辑”分开。当所有条件只是基于一个值去查结果时字典就是比一串if-elif更合适的数据结构。我用这个思路重构过不少老代码维护性提升非常明显。5.2 案例二多条件组合判断的简化过程再看一个稍复杂的场景判断一笔交易是否允许自动通过。规则有三条用户等级不低于3级单笔金额不超过5000元用户近30天无恶意退款记录新手写法常常是嵌套if user.level 3: if order.amount 5000: if not user.has_bad_refund_record(days30): auto_approve(order)这种写法每加一个条件就多一层缩进很快代码右移得没法看。推荐的写法是“守卫子句”把例外情况先拦住正常流程放在后面def should_auto_approve(user, order): if user.level 3: return False if order.amount 5000: return False if user.has_bad_refund_record(days30): return False return True然后在调用处if should_auto_approve(user, order): auto_approve(order)这样改造后每个规则都是独立的判断不需要层层缩进新加条件也只是在函数里再加一个if。而且should_auto_approve这个函数可以独立写单元测试这是嵌套写法做不到的。5.3 案例三多个elif换成数据驱动之后还有一类常见情况是多个elif判断的其实是同一类数据的区间或组合。比如根据库存数量生成提示语if stock 0: tip 缺货 elif stock 10: tip 库存紧张 elif stock 100: tip 库存正常 else: tip 库存充足这种区间型判断用字典没法直接映射因为条件不是等值比较。此时可以用一个有序元组列表来处理stock_rules [ (0, 缺货), (10, 库存紧张), (100, 库存正常), ] stock_tip 库存充足 for limit, tip in stock_rules: if stock limit: stock_tip tip break这种写法把规则数据集中在一起后续调整阈值时改数据就行不用改动流程代码。但说实话如果规则只有三四个写成一行列表反而有点绕。数据驱动适合规则数量多、且常常变化的需求。5.4 案例四量化交易策略里的条件信号量化回测中经常要判断信号是买入、卖出还是持有这跟条件语句太契合了。假设我们写一个简单的均线策略价格上穿20日均线买入下穿20日均线卖出。条件判断的核心逻辑可能是这样的previous_cross price[0] - ma20[0] # 昨日差值 current_cross price[1] - ma20[1] # 今日差值 if previous_cross 0 and current_cross 0: signal BUY elif previous_cross 0 and current_cross 0: signal SELL else: signal HOLD这里的判定逻辑非常依赖“上穿”和“下穿”的边界条件。如果只用今天的价格大于均线就判断买入那在均线附近震荡时会产生大量假信号。实际策略框架里经常会加上过滤条件比如连续几根K线确认、成交量配合等。每加一个过滤条件就是在扩充条件语句的复杂度。处理这类问题时我特别推荐先把每个判断条件写成单独的函数然后用卫语句组织起来。信号逻辑本身就复杂如果不梳理代码很容易失控。6. 条件语句常见问题与调试排查技巧6.1 条件为False但程序却走进了分支这类问题通常都出在真假值判断上。举个例子items None if not items: do_something()如果预期是“items为空列表时才执行”但items是None这个判断同样成立行为就偏离预期了。更合理的写法是if items is None or len(items) 0: do_something()或者直接用if not items但要明确知道它把None和空容器都拦住了。这种“隐式真假值把不同情况混在一起”的问题排查的时候可以先将条件的各个组成部分分别打印出来print(fitems{items!r}, bool(items){bool(items)})肉眼确认后再决定用哪种判断方式。6.2 缩进引起的逻辑错位怎么快速定位缩进引发的bug不只是IndentationError报错更可怕的是代码能运行但逻辑完全不是你想要的。比如if x 0: print(positive) y x * 2 print(always)如果print(always)这一行不小心多了一个空格它就会进入if块结果变量y不存在时还会抛NameError。这种问题在真实项目里特别容易出现在“git merge之后”因为合并冲突时缩进容易错乱。排查手段有几种。第一编辑器里开启“显示空格”功能用点状符号区分空格和Tab。第二在重要分支前后加日志输出确认执行顺序。第三用python -m py_compile编译一遍语法问题立刻暴露但如果是逻辑问题编译过也不代表对。最有效的还是缩小排查范围如果一段代码行为不符合预期把所有的if块先用二分法注释掉一半看问题是否消失。6.3 条件写得太长导致调试困难条件表达式一行超过80个字符甚至拖到几行调试时就很难判断到底哪部分出了问题。if order.user.vip_level 3 and order.payment.method in (alipay, wechat) and not order.discount.coupon_expired and order.shipping.address.province ! 海外:这种代码我的建议只有一个拆。先把每个子条件赋值给有名字的变量is_vip order.user.vip_level 3 is_supported_payment order.payment.method in (alipay, wechat) coupon_valid not order.discount.coupon_expired is_domestic order.shipping.address.province ! 海外 if is_vip and is_supported_payment and coupon_valid and is_domestic: ...这样做有几个好处变量名本身就是注释分支进入不了的时候可以直接print出这几个布尔值立刻锁定哪个条件不满足以后改逻辑时也不需要在一长串表达式里挑逗号。6.4 优先级与比较链的隐藏陷阱Python支持连续比较比如a b c这在数学上很自然但其实它等价于a b and b c而且中间的b只会被求值一次。这个语法在很多语言里不支持新手容易误以为会报错实际它是在Python里合法且有明确语义的。但连续比较也有隐藏问题。看这个例子if 0 x 100: print(范围内)看起来完全没问题。但如果中间表达式有副作用或者涉及函数调用连续比较会被展开成多个判断执行次数可能不一样。比如if low() get_value() high():get_value()只会执行一次但展开成low() get_value() and get_value() high()后get_value()还是只执行一次。真正的隐患是如果你拆开写if low() get_value() and get_value() high():那get_value()会执行两次。如果这个函数返回的值在两次调用间有变化就可能出现逻辑bug。所以日常写代码如果值不是固定不变的尽量先存到变量里再做比较。6.5 调试断点下在条件语句的正确位置调试条件代码时我经常会看到有人把断点打在if这一行然后一步步走进去这是可以的。更高效的做法是在分支内部打条件断点比如你想看age 18时才停下来的情况直接在if块内第一行下一断点断点属性里设置条件age 18。这样循环里跑多少遍都不怕只有当条件成立时才会中断。这个技巧在PyCharm和VS Code里都支持非常实用。如果项目使用pdb也可以用命令来设置条件断点效果类似。不过绝大多数场景下IDE的图形化条件断点已经够用了。条件语句调试失败次数多了以后我养成一个习惯任何复杂的条件分支在写完之后都会用一个最简单的测试数据跑一遍确认走到了预期分支再交给测试。7. 避坑清单这些坑我基本都踩过给一份独家避坑清单每条都是我在实际项目里摔过的经验。关于真假值不要用if x判断None要用if x is None不要用if len(list)判断列表是否为空直接if list更Pythonic不要用if x NonePEP 8不推荐语义也不如is None严谨自定义类时如果实现了__bool__或__len__它放到if里的表现可能和你的直觉不一样要有意识去检查关于分支结构区间判断从宽到严排或者从严到宽排选定一种并保持一致elif本质是“互斥”不是“继续判断”要理解它与连续if的差别连续if和elif混用时每个if都是独立判断可能导致一段数据进入多个分支这不是bug但可能是你设计上的疏漏关于代码风格缩进只用四个空格编辑器统一设置保存时清理行尾空白复杂条件拆成有名字的变量不要写超过一行或语义模糊的长表达式三元表达式只用于简单二选一嵌套三元尽量别碰可读性杀手关于语法特性Python 3.10以下的版本不支持match-case公司项目升级前要确认兼容性海象运算符:在if条件里虽然合法但注意加括号避免优先级问题不要依赖小整数is比较的缓存机制永远用比较值7.1 条件分支里没有else时的问题有时候代码只写了if没有else。这不是语法错误但语义上可能有问题。比如if user.is_admin: action admin_panel当用户不是管理员时action变量根本不存在后面一旦访问action就会NameError。这种情况最好在写if的同时想清楚“不满足时应该是什么”哪怕只是if user.is_admin: action admin_panel else: action normal_panel或者提前给action一个默认值。少了else本身没有错但逻辑不完整往往给后续代码埋坑。7.2 多逻辑分支时不要把所有情况都交给else我见过这样的代码if status success: pass elif status failed: pass elif status timeout: pass else: pass最后一个else什么也不做整个判断分支形同虚设。更合理的做法是明确枚举出所有已知状态最后用else兜底同时在else里打日志或抛异常防止静默吞掉未知情况。尤其是在接口对接场景里上游可能随时新增状态字段如果没有日志察觉问题会被掩盖很久。7.3 用日志输出分支走向排查复杂业务逻辑在关键的分支入口加一行日志成本很低但价值极大。logger.info(order status check: status%s, user%s, order.status, user.id) if should_approve(user, order): approve(order) else: reject(order)排查问题时第一件事就是看日志里这个订单走了哪个分支。如果没有日志你就要靠猜和复现效率低得多。等业务跑一段时间日志稳定后可以通过统计日志里各个分支的频次反过来验证业务流程是否和预期一致。这个习惯在多数正规团队都是必备的。8. 条件语句在项目组织层面的设计思路8.1 把判断逻辑封装成函数而不是散落各处条件语句最大的风险不是语法而是散落。同一个业务规则今天在views.py里写一份明天在service.py里又写一份后天在任务队列里再写一份。三份逻辑稍有不一致线上就是事故。我推荐的做法是凡是涉及业务规则的判断集中封装成带清晰函数名的函数。比如def is_eligible_for_refund(order): 是否允许退款 return order.payment_status paid and not order.refunded def is_flash_sale_active(nowNone): now now or datetime.now() return flash_sale_start now flash_sale_end调用处统一使用函数而不是再写一遍原始条件。这样改动规则时只需要改一个地方测试也只需要针对一个函数。8.2 条件分支过多时用策略模式或字典映射当if-elif分支数量膨胀到十几个时代码已经不太适合用简单的条件语句硬撑了。此时可以根据业务类型考虑用字典映射函数或者策略模式。举个简单例子。假设处理不同文件类型的解析逻辑def parse_file(file_type, path): if file_type json: return parse_json(path) elif file_type csv: return parse_csv(path) elif file_type yaml: return parse_yaml(path) elif file_type xml: return parse_xml(path)改成字典映射parsers { json: parse_json, csv: parse_csv, yaml: parse_yaml, xml: parse_xml, } def parse_file(file_type, path): parser parsers.get(file_type) if parser is None: raise ValueError(fUnsupported file type: {file_type}) return parser(path)这样新增一种格式只需要在字典里加一行映射不需要动函数主体。判断逻辑本身没有消失但它从“一堆elif”变成了“一张数据表”维护成本变了。8.3 条件判断中的性能考量与重复计算在循环、高频调用路径中条件判断的性能问题才会真正显现。最常见的问题是同一条件在循环体内被反复计算。for item in orders: if item.user.vip_level 3 and discount_map.get(item.user.id): apply_discount(item)如果discount_map.get(item.user.id)非常耗时这段代码就会在每次循环里重复计算相同的结果。优化方式是先把所有VIP用户的折扣结果算好存起来循环时只查结果vip_discounts {} for user_id, discount in discount_map.items(): if users[user_id].vip_level 3: vip_discounts[user_id] discount for item in orders: if item.user.id in vip_discounts: apply_discount(item)第二个优化点是条件排列顺序。把开销最小的比较放前面开销大的比较放后面就能在大部分情况下避免执行慢判断。比如先判断user.is_active再判断user.last_login_time threshold因为前者只是读一个布尔值后者可能涉及时间解析和比较。不过这两项优化都属于“微优化”只有在profiler确认是热点时才值得折腾。平时最该做的是避免明显的重复计算。8.4 测试条件分支的覆盖策略条件语句写得好不好测试最直观。对于一个if-elif-else结构最少要覆盖这几类用例第一个分支满足的情况中间某个分支满足的情况所有条件都不满足、走else的情况边界值恰好等于阈值的情况异常类型的数据比如None、空字符串、0值在pytest里用参数化可以把这些用例写得很清爽import pytest pytest.mark.parametrize(score,expected, [ (95, 优), (85, 良), (60, 及格), (59, 不及格), (0, 不及格), (-1, 不及格), ]) def test_grade(score, expected): assert grade_of(score) expected这种测试跑一遍所有分支都走了一遍。一旦以后改了阈值或新增分支测试能第一时间告诉你哪些场景行为变了。从我维护过的一些老项目来看**条件语句相关的bug绝大多数不是语法错误而是“条件写对了但顺序不对”“真假值理解错了”“分支漏了一种场景”**这三类。把核心逻辑封装成函数、用数据表驱动判断、为每个分支设计测试这三板斧能消灭绝大部分隐患。最后再分享一个小技巧写完条件判断后试着把每个分支的“输入”和“预期输出”在注释里标一下。不需要长篇大论就写一两行例子的数据比如(80, 良)、(100, 优)等你自己三个月后再回来看这段代码时会发现这些注释比什么都管用。条件语句的坑说到底不是一个语法问题而是逻辑设计问题。想清楚再写比事后排查要划算得多。
返回列表