资讯动态

Python控制流与函数:分支、循环与模块化编程实战

发布时间:2026/10/15 9:01:02 来源:尧图企业网站定制
很多人自学 Python第一周就被卡在一个奇怪的地方语法看懂了例题抄会了一到自己写就不知道从哪儿下手。我见过不少初学者问题几乎都出在同一个环节——Python控制流与函数这两个概念他们只是见过并没有真正用顺手。说白了控制流是让代码拥有判断力和反复执行能力的机制函数是让代码拥有组织力的机制。没有它们你写出来的程序只能是自上而下一路走到黑稍微复杂一点的需求就完全没法落地。这篇内容我尽量用大白话、实际案例和踩坑经验来讲适合零基础刚学完变量和数据类型的人也适合学得半懂不懂、想重新把逻辑捋顺的人。我会把分支、循环、函数拆开讲透每一步都告诉你为什么这么写而不是丢一堆代码让你抄。最后还有一个综合小项目把这三样东西串起来你可以照着敲一遍敲完基本就算真正入门了。1. 先搞清楚控制流到底在控制什么1.1 从顺序执行到让程序自己拿主意计算机程序最基本的执行方式是顺序执行就是一行一行往下跑。这个没什么好说的但现实中的问题基本都不是直线型的。举个日常例子你早上出门如果下雨就带伞否则就不带。这是一次判断。再比如你要把一箱苹果挨个检查一遍好的放左边坏的放右边这就是循环判断的组合。控制流就是程序里的交通规则。它负责决定代码走哪条路、走几次、什么时候停下来。Python 的控制流主要分成两类一类是分支if让代码根据条件选择执行不同的语句块另一类是循环for、while让代码重复执行某段语句块直到条件不成立。你可能会觉得这有什么难的但初学者真正的坎在于写代码时需要模拟程序逐行执行的心态。你得把自己当成 Python 解释器一步步看它究竟走了哪条分支、循环了几次、变量变成了什么值。很多人代码跑不出预期结果就是因为他脑中的执行流程和实际流程不一致。1.2 条件判断的基础布尔值与比较运算不管是if还是while核心都离不开一个东西条件。而条件求值的结果只有一个 ——布尔值True或False。布尔值在 Python 里是一种独立的数据类型只有两个取值True和False并且注意首字母大写不能写成true或false。比较运算符是产生布尔值最常用的方式运算符含义示例等于score 100!不等于name ! 大于age 18小于price 50大于等于count 10小于等于temperature 36.5还有一个经常被忽略的点是比较两个值是否相等而是赋值这个错误几乎每个初学者都会遇到后面我会专门说。多个条件组合时可以用逻辑运算符and、or、not。and表示条件同时成立or表示至少一个成立not表示取反。比如age 25 salary 8000 if age 18 and salary 5000: print(符合条件)这里要注意求值顺序not优先于andand优先于or。为了不让自己和读代码的人晕复杂条件建议加括号比如(age 18) and (salary 5000)。虽然结果一样但可读性完全不同。1.3 新手最容易混淆的真值判断细节除了显式的比较运算Python 还允许直接把任意数据当条件用。这就是真值判断逻辑是非空即真非零即真。数字0、0.0被视为False其他数字都是True空字符串、空列表[]、空字典{}、空元组()被视为False有内容就是TrueNone永远被视为False这个特性可以让代码非常简洁。比如判断一个列表是不是空的items [] if items: # 等价于 if len(items) 0: print(列表里有东西) else: print(列表是空的)很多人第一次看到if items:会愣一下items 明明是个列表怎么能当条件原因就是上面说的真值判断。bool(items)返回False所以走进了else。我见过不少初学者在这里踩坑他们以为if items是判断列表是否存在但 Python 里变量只要定义了就存在真正的含义是列表内容是否非空。理解了这个区别很多迷惑瞬间就能解开。2. 分支选择if...elif...else的语法与思维2.1 分支骨架与缩进为什么不能乱来if语句是分支的基础标准结构长这样score 85 if score 90: grade 优秀 elif score 60: grade 合格 else: grade 不合格 print(grade)这里的elif是 Python 特有的写法完整意思就是否则如果。其他语言里常见的是else ifPython 为了简洁直接合成一个关键字。要注意的是if-elif-else整体是一个语句只会选择其中一个分支执行从上往下匹配一旦某个条件成立后面的elif和else全都跳过。另外一个极其重要的点缩进是 Python 语法的一部分。在 Java、C 这类语言里缩进纯粹是习惯代码块靠花括号分隔但在 Python 里缩进直接决定语句属于哪个分支。if score 60: print(及格) print(这句也在if里) print(这句在if外面)注意上面第三个print和if在同一层缩进所以不管条件成立与否它都会执行。很多初学者以为多缩进一格只是美观问题结果代码逻辑完全变了。PEP 8 推荐用 4 个空格作为一级缩进不要混用 Tab 和空格否则解释器会报IndentationError。2.2 条件顺序、elif语义与重复分支的坑初学者写多条件分支时容易犯一个毛病条件顺序随便排。我们看个例子age 70 if age 18: print(成年人) elif age 60: print(老年人)这段代码有问题。当age 70时第一个条件age 18已经成立了所以会输出成年人永远不会走到elif。写多分支时要注意条件的覆盖范围和先后顺序通常做法是从小范围、特殊的情况开始判断把大范围、通用的条件放最后。上面的例子应该反过来age 70 if age 60: print(老年人) elif age 18: print(成年人) else: print(未成年人)还有一个常见的逻辑错误是本来想用多分支结果写成了多个独立的if。这两者的区别非常关键# 错误思路多个独立if score 85 if score 60: print(及格) if score 80: print(良好) if score 90: print(优秀) # 输出的是及格和良好因为三个if都会依次判断但用if-elif就只会输出其中一个结果。具体用哪种取决于你的业务语义。是可能同时触发多个动作还是只能落入一个分类前者用多个if后者用if-elif-else。这个区分想明白分支就通了一大半。2.3 用一个小例子串起分支逻辑我们写一个实际点的东西根据 BMI 判断身体状态。BMI 的计算公式是体重公斤除以身高米的平方。weight 65 height 1.75 bmi weight / (height ** 2) print(BMI:, round(bmi, 1)) if bmi 18.5: result 偏瘦 elif bmi 24: result 正常 elif bmi 28: result 偏胖 else: result 肥胖 print(状态:, result)我把判断顺序设计成从小到大排列 18.5先判断然后 24、 28最后落到else。因为elif是自上而下匹配的这个顺序保证了每个区间互不重叠。如果你把顺序反过来写先写bmi 28那么所有小于 28 的人都进这个分支了后面的逻辑全废。分支结构本身不难难的是把现实规则翻译成排好序的条件。我的建议是写之前先在纸上画出区间或者用注释把条件顺序列出来再动手写代码。3. for与while玩转迭代与循环控制3.1 for循环的本质是可迭代对象for循环在 Python 里的设计非常通用它的语法是for 变量 in 可迭代对象: 循环体可迭代对象可以粗略理解成能一个一个拿出来东西的容器。字符串、列表、元组、字典、集合甚至文件对象都属于可迭代对象。所以你可以直接遍历字符串里的每个字符for ch in hello: print(ch)这段会依次输出h、e、l、l、o。初学者第一次看到可能会觉得奇怪——字符串不是一个整体吗怎么还能遍历其实字符串本质是一个字符序列Python 里的序列类型都可以被迭代。这个观念早点建立后面学文件读取、数据分析时会顺畅得多。遍历字典也很有意思。默认情况下遍历的是键但你可以显式选择user {name: 张三, age: 20} for key in user: # 遍历键 print(key) for value in user.values(): # 遍历值 print(value) for key, value in user.items(): # 同时遍历键和值 print(key, value)注意最后一种写法user.items()返回的每个元素是一个键值对元组for key, value in ...这种写法叫元组解包在遍历字典时非常常用。3.2 range、break、continue与循环elsefor循环搭配range()是最经典的组合。range()有三种常见用法range(5) # 生成 0, 1, 2, 3, 4 range(1, 5) # 生成 1, 2, 3, 4 range(1, 6, 2) # 生成 1, 3, 5初学者不太习惯 Python 的右开区间规则range(start, stop)包含start但不包含stop。我第一次也觉得很别扭后来记住一个技巧range生成的元素个数等于stop - start当步长为 1 时比如range(1, 5)就是 4 个元素对应 1、2、3、4。break和continue是循环体内的两个控制工具。break是立即结束整个循环continue是跳过当前这一次直接进入下一次迭代。for i in range(1, 10): if i % 3 0: continue # 能被3整除的数字不打印 if i 8: break # 超过8就直接退出循环 print(i)输出结果是 1、2、4、5、7。这里还有一个挺冷门但很实用的语法for循环也有else子句。它的触发条件是循环正常执行完毕没有被 break 打断才执行else里的代码。如果循环是被break提前结束的else就不会执行。这个特性适合做查找是否存在之类的场景比如在一堆数字里找某个目标numbers [3, 7, 12, 18, 25] target 14 for n in numbers: if n target: print(找到了, n) break else: print(没找到目标)这段代码如果找到了目标break会退出循环else不执行如果全找完都没有else执行。用for-else表达这个逻辑比设一个flag变量再用if判断要干净得多。3.3 while循环的适用场景与死循环防范while循环的逻辑是只要条件为真就一直执行循环体。count 0 while count 5: print(当前计数:, count) count 1什么时候用while、什么时候用for我的经验是如果循环次数事先知道优先用for如果次数取决于某个动态条件用while。比如不断读取用户的输入直到用户输入 q 才退出这种场景天然就是whilemessage while message ! q: message input(请输入内容输入q退出: ) print(你输入了:, message)while最大的隐患是死循环。如果循环体内没有让条件最终变成False的语句程序就会永远跑下去。上面这个例子如果input一直不返回q循环就永不停止。这不一定都是坏事有些程序比如网络服务本来就该常驻运行但对于新手练习来说死循环多半是 bug。防范死循环有两个常用技巧在循环体里检查计数器是否在变化给循环设置一个最大迭代次数比如while count 1000即使逻辑出了问题也会自动停如果你不小心跑出了死循环在终端里按CtrlC可以中断程序这个快捷键是新手保命技能。4. 函数从重复代码到模块化思维4.1 def的完整拆解与参数传递方式函数在初学者眼里往往被简化成把重复代码装起来但它的真正价值是模块化思维把一个复杂任务拆成若干子任务每个子任务用函数封装让主流程变得非常清爽。定义函数的语法def add(x, y): result x y return resultdef是关键字add是函数名x和y是参数return是返回值。调用时直接写add(3, 5)结果就是 8。参数传递有两种常见方式。一种是位置参数按顺序传add(3, 5)x3, y5。另一种是关键字参数传的时候明确指定哪个参数是哪个值add(x3, y5)关键字参数的好处是可读性高调用的时候一眼就能看出每个值的意义。还可以在定义函数时给参数设置默认值def greet(name, greeting你好): print(greeting , name)这样greet(张三)输出你好, 张三greet(李四, 早上好)输出早上好, 李四。带默认值的参数必须放在不带默认值参数的后面否则 Python 直接报语法错。4.2 return的深层含义与默认值陷阱return的作用有两个一是立即结束函数的执行二是把结果交给调用者。如果函数里没有returnPython 会自动返回None。所以很多人写函数时发现自己拿到了None往往就是写返回值的时候漏了return。另外有些初学者问函数里能不能有多个return可以但同一时刻执行到哪个return函数就在哪里结束了。一般多分支结构的函数会写成这样def health_status(bmi): if bmi 18.5: return 偏瘦 elif bmi 24: return 正常 else: return 偏胖这段代码更简洁但要注意这和你先算一个result变量、最后统一return result是等价的。哪种写法好个人习惯不同但对于简单函数我倾向于直接用多个return提前返回逻辑更直观。还有一个值得提的坑默认参数的可变对象陷阱。如果你在默认参数里写一个可变对象比如列表或字典多次调用函数时会共享同一个对象出现奇怪的结果def append_item(item, items[]): items.append(item) return items print(append_item(1)) # [1] print(append_item(2)) # [1, 2] —— 第二次调用时列表还是上一次的第一次调用输出[1]第二次调用输出[1, 2]第三次是[1, 2, 3]完全不是你以为的每次都从空列表开始。原因是默认列表只创建了一次后续调用都在修改同一个列表。正确做法是默认值设为None函数内部再判空创建def append_item(item, itemsNone): if items is None: items [] items.append(item) return items这个坑对于入门阶段来说有点超前但我见过太多人掉进去提前说一次省得你以后踩。4.3 局部变量、全局变量与作用域直觉函数内部定义的变量叫局部变量它只在函数内部生效。函数外部定义的变量叫全局变量。初学者最容易困惑的问题是函数内部能不能修改全局变量total 0 def add_to_total(n): total total n这段代码其实会报错。因为在函数内部给total赋值时Python 会把它当成一个全新的局部变量但这个局部变量又还没有定义于是报UnboundLocalError。如果你想在函数内部修改全局变量需要先声明total 0 def add_to_total(n): global total total total n但是我强烈建议初学者少用global。全局变量被多个函数修改后程序的状态会变得极难追踪。更推荐的做法是函数通过参数接收值通过return返回值这样数据流向是明确的。total 0 def add_to_total(current_total, n): return current_total n total add_to_total(total, 5)这几个函数都在函数内做纯计算不依赖外部状态代码更好理解也更好测试。养成这种习惯对以后读别人源码、写大型项目都非常有帮助。5. 综合实战用控制流和函数写一个猜数字小游戏5.1 功能拆解与自顶向下设计理论讲了不少现在我们把控制流和函数真正结合起来写一个猜数字游戏。需求是这样的程序随机生成一个 1 到 100 之间的整数玩家反复猜程序提示猜大了或猜小了直到猜中为止然后输出总共猜了多少次。拿到需求不要急着写代码先拆功能。我习惯用自顶向下的方式把整个任务分成几个子任务每个子任务对应一个或多个函数。生成随机数用random.randint(1, 100)读取玩家输入可能需要反复读取直到输入合法比较数字并给出提示猜大了、猜小了、猜中了统计猜测次数用一个计数器每次输入后加一输出最终结果这种拆解虽然简单但培养的是结构化思维。你以后写任何程序都可以先问自己这个程序到底要做几件事每一件事能不能单独用函数实现5.2 完整代码实现与运行效果下面是一份完整可运行的代码我尽量写得可读性好一些import random def get_guess(): 获取玩家输入并确保输入是整数 while True: text input(请输入你猜的数字: ) if text.isdigit(): return int(text) print(输入无效请重新输入) def check_answer(guess, target): 比较猜测值和目标值返回提示信息 if guess target: return 猜大了 elif guess target: return 猜小了 else: return 恭喜你猜对了 def play_game(): target random.randint(1, 100) attempts 0 while True: guess get_guess() attempts 1 message check_answer(guess, target) print(message) if guess target: break print(你总共猜了, attempts, 次) play_game()这里的核心结构是两层嵌套外层while True负责保证游戏不结束直到猜中内层get_guess里的while True负责确保输入合法。play_game函数把整个流程串起来而具体的比较逻辑被放到了check_answer里。运行效果大概是这样的请输入你猜的数字: abc 输入无效请重新输入 请输入你猜的数字: 50 猜小了 请输入你猜的数字: 80 猜大了 请输入你猜的数字: 65 恭喜你猜对了 你总共猜了 3 次5.3 一题多解对比环环相扣的思维能力同样的功能也有人会全部写在主流程里不用任何函数。代码能用但问题在于所有逻辑揉在一起一旦出现 bug你得从头读到尾才能定位问题。我建议你养成一个函数只干一件事的习惯。比如get_guess只负责拿输入check_answer只负责比较并返回消息。如果你想升级功能比如限制最多猜 10 次、记录每次猜测历史只需要改动对应的函数不影响其他部分。这就是函数带来的维护优势。再换一种思路如果我想限制最大猜测次数可以在play_game里加一个计数器判断def play_game(): target random.randint(1, 100) attempts 0 max_attempts 7 while attempts max_attempts: guess get_guess() attempts 1 message check_answer(guess, target) print(message) if guess target: print(你总共猜了, attempts, 次) return print(机会用完了正确答案是, target)注意我把while True改成了while attempts max_attempts并且猜中时直接return让函数提前结束。这样既防了死循环也自然处理了机会用完的情况。同样是循环和函数的组合稍微改一下条件程序的健壮性就完全不同。6. 初学者避坑指南我在实际中见到的典型错误6.1 语法错误与逻辑错误的区分程序出了问题第一步不是改代码而是判断这属于哪类问题。语法错误是 Python 解释器根本读不懂你的代码会直接报错并指出位置逻辑错误是代码能运行但结果和你想的不一样。两者的调试方法完全不同。语法错误最常见的有这么几种少了冒号比如if score 60应该写成if score 60:缩进不一致混用了 Tab 和空格括号不匹配左括号和右括号数量对不上中英文符号混淆比如写了中文的冒号或中文括号我是强烈建议初学者遇到报错后先看报错信息的最后几行那里通常有具体的行号和错误类型。很多人一看到红字就慌了其实 Python 报错已经非常友好了比如提示IndentationError: expected an indented block意思就是这里该缩进的没缩进。逻辑错误更隐蔽。比如你写了if score 85:这不是语法错误因为score 85是一个赋值语句Python 不会报错但它的真值永远为真程序走的分支永远不对。这种问题靠肉眼很难发现需要系统性的调试方法。6.2 用print调试的实用姿势针对逻辑错误最适合初学者的调试方式就是把关键变量的值打印出来俗称print 大法。不要觉得这很低级很多实际工作中复杂 bug 的第一版定位也是靠打印日志。调试思路是这样的在可能有问题的位置前后打印变量的中间状态然后运行程序对比实际输出和你的预期。比如你怀疑循环次数不对就在循环体开头打印计数器的值怀疑分支走错就在每个分支入口打印条件结果。for i in range(3): print([调试] 当前 i , i) if i % 2 0: print([调试] 进入偶数分支)但有一点要提醒调试完记得把临时打印删掉或者用注释标记。我见过不少初学者的代码里留着大量print调试语句跑出来的结果夹杂着调试输出很难看清正常数据。习惯规范一点调试代码用统一的print([debug], ...)前缀最后全局搜索删掉最省事。6.3 常见问题对照表我把入门阶段最常见的错误整理成一张表方便你对照自查问题现象可能原因解决方案NameError: name xxx is not defined变量名拼错或未定义检查变量名拼写确认赋值语句已经执行IndentationError缩进错误混用 Tab 和空格统一用 4 个空格编辑器开启空格显示SyntaxError: invalid syntax冒号缺失、括号不匹配检查上一行语法尤其是if、for、def后的冒号TypeError: unsupported operand type(s)不同类型的数据做了运算用type()确认变量类型必要时用int()转换分支结果永远不对写成了检查比较语句赋值用单等号比较用双等号循环次数少了一次或多了一次range边界理解错误记住右开区间规则range(1, 5)不含 5死循环循环条件永远为 True检查循环体内条件变量是否在变化还要多提一嘴逻辑顺序int(abc)会报ValueError所以我在猜数字游戏里用isdigit()先判断输入是不是纯数字这就是先验证再使用的防御式写法。这种意识越早养成越好。关于调试习惯我最深的体会是报错信息是最快的老师。不要一报错就急着去百度先自己读一遍报错把问题定位在某个文件和某一行再查具体机制。能自己定位错误在哪一行和知道这个错误怎么解决是两个不同的能力前者练的是系统的拆解思维后者只是经验积累。你把前面这些例子全部自己敲一遍报错、分析、修复的过程走几轮比看多少篇教程都管用。最后再补充一个小技巧代码不要一次性写太长。写一个函数就运行一下写一个循环就测试一下。每次都保证当前这段代码是对的再写下一段。我见过太多新手一口气写七八十行最后跑起来全是错又不知道错在哪一步回头一行行查特别痛苦。小步快跑才是新手最快的进阶路。

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

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

免费获取报价 →
↑