尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Python循环全攻略:for、while、break与嵌套实战

发布时间:2026/9/26 13:03:03

资讯中心
01
ARTICLE

Python循环全攻略:for、while、break与嵌套实战

Python循环全攻略:for、while、break与嵌套实战
写Python写了几年我最深的体会就是循环不是语法是思维方式。刚学Python时总爱把相同代码复制粘贴好几遍后来才明白凡是你写出第二遍重复代码的时候就该停下来想想——循环是不是能帮你把这活儿干了。Python里循环就两个主将for和while看起来简单真要在实战中用好里面藏着不少细节和坑。这篇文章不会给你堆概念直接把日常开发里最常见的循环场景、代码写法、翻车现场都过一遍尤其适合刚学Python不久、正卡在“不知道循环该咋嵌套”“while怎么又死循环了”这些阶段的同学。你要是已经写了一阵子也能从后面的优化和排错里捞到一些平时没注意的点。1. 先想清楚什么样的问题该交给循环1.1 重复劳动的本质其实就只有两类人最不擅长的事情之一就是长时间做一模一样的机械动作。比如给一百个文件改格式、算一百个人的平均分、把一段话拼接一百遍这种事手工做又慢又容易错。计算机恰恰相反它唯一不嫌烦的本事就是快速重复。所以编程里第一课不是变量也不是函数而是循环——它解决的就是“重复”这件事。Python里的重复任务可以归结成两种第一种是“我知道要重复多少次”比如打印1到100、遍历列表里的每个元素第二种是“我不知道次数但我知道什么时候该停”比如一直读取用户输入直到输入合法为止。for循环天然适合第一种while循环天然适合第二种。理解了这个分类你就不会在那纠结选哪个了。生活里有个特别形象的类比你在洗衣房把脏衣服扔进洗衣机设定40分钟洗完。这就像for循环次数是固定的到时间就结束。另一种情况是手洗你一边搓一边看还有没有污渍没干净就继续搓干净了才停。这是while循环条件是“衣服是否干净”。1.2 for和while的选择标准一张表就够了很多新手一上来就问“for和while有什么区别”其实区别没那么玄乎。你只要在写循环前问自己一句我清楚要循环多少次吗如果清楚比如“遍历列表里的每一项”“生成0到99的整数”直接用for。如果不清楚比如“不断问客户还要不要继续下单直到他说不要为止”那就用while。但这个边界不是绝对的while也可以模拟固定次数for也可以模拟条件退出只是那样写会把代码变拧巴维护的人也会一边看一边骂。对比点for循环while循环适合场景遍历可迭代对象、循环次数已知循环次数未知、依赖条件判断核心机制从可迭代对象中逐个取出元素判断条件是否为真真则继续执行常见风险很少死循环但可能忘记break结束忘记更新条件值容易无限循环代码可读性语义清晰适合表达“每个元素”适合表达“只要满足条件就一直做”举个例子你要打印1到10用for和while都能写但for更直观# for 写法 for i in range(1, 11): print(i) # while 写法 i 1 while i 10: print(i) i 1 # 这一行忘了就完了看到没有同样的功能while循环多了一行i 1。这行代码就是循环的“推进器”没了它i永远等于1条件永远成立程序就死在里面了。所以我平时在团队里经常说能用for就优先用for因为for帮你把推进器藏在迭代器内部从根源上减少一半死循环风险。2. for、while、break、continue核心语法一口气讲透2.1 range不是列表而是一个会“按需生产”的计数器Python里最常用的循环搭档就是range。很多初学者以为range(10)会生成一个列表[0, 1, 2, ..., 9]其实在Python 3里range()返回的是一个惰性对象它不会一次性在内存里装一堆数字而是每轮循环到了才生成下一个数字。这就好比自助餐厅的寿司师傅你吃一个他捏一个而不是提前捏一千个摆在那里等你。range的用法要记牢range(stop)生成0到stop-1range(start, stop)生成start到stop-1range(start, stop, step)用来控制步长。这里特别容易踩的坑就是“结尾不包含”。range(1, 10)只到9不会到10。如果你想要1到10必须写range(1, 11)。我见过无数次测试用例因为差一个数而挂掉基本都是这个原因。# 常见的range三种用法 for i in range(5): # 0,1,2,3,4 print(i) for i in range(2, 8): # 2,3,4,5,6,7 print(i) for i in range(0, 20, 5): # 0,5,10,15 print(i)除range外字符串、列表、元组、字典、集合都可以直接放进for循环里遍历。字典默认遍历的是键想遍历键值对就用for k, v in dict.items()。这些都是可迭代对象它们背后有个统一的“迭代器协议”简单理解就是for循环每次向对象要“下一个元素”对象乖乖给一个直到给完了循环自己结束。所以for循环天然不会越界也不容易出现索引溢出。2.2 while的条件是活开关你得自己控制开关while循环的结构特别单纯只要条件为真就一直执行循环体。这个条件就像一扇门的开关它在每次循环开始时都会被重新检查一次。你不主动改条件里的变量门就一直开着程序就永远出不去。经典场景是写一个“用户输入校验”。比如你做一个登录界面要求用户输入密码密码不对就得重来。这种循环次数不确定因为谁也不知道用户第几次输对。用while写相当自然password while password ! abc123: password input(请输入密码) print(登录成功)这里用户不输对程序就一直卡在input那里。但要是用户就是想不起来密码这程序会无限问下去所以实际开发中通常会加一个“尝试次数限制”attempt 0 while True: password input(请输入密码) attempt 1 if password abc123: print(登录成功) break if attempt 3: print(尝试次数过多退出) breakwhile True配break是一种非常实用的写法。把退出条件散落在循环体内部而不是堆在while条件那一行逻辑会更清楚。很多人不推荐无限循环但我觉得只要break逻辑清晰这反而是最易读的方案。2.3 break、continue、else三兄弟各有各的分工break和continue是循环里的两大控制令牌。break的意思是“立刻结束整个循环后边不玩了”continue的意思是“跳过本轮剩余代码直接进入下一轮”。它们的区别可以想象成跑步break是裁判吹哨中止比赛所有人都不能跑了continue是跳过眼前这个栏杆继续往前冲。举个例子你要从列表里找第一个能被7整除的数找到就停这时用break最干脆numbers [12, 33, 49, 18, 70] for num in numbers: if num % 7 0: print(找到了, num) breakcontinue适合“过滤”场景。比如只打印奇数偶数直接跳过for i in range(1, 11): if i % 2 0: continue print(i)这里遇到偶数时continue让print(i)不被执行但循环本身不会断。如果把continue换成break打印1之后整个循环就结束了结果完全两样。Python还有一个容易被忽视的语法for...else和while...else。这里的else不是“否则”而是“循环正常跑完才执行”。如果循环是被break中断的else就不会执行。这个特性用来判断“循环里有没有找到东西”非常顺手。比如判断一个数是不是素数标准写法就用到它num 17 for i in range(2, int(num ** 0.5) 1): if num % i 0: print(不是素数) break else: print(是素数)这个代码的精妙之处在于如果循环里发现能整除立刻break对应的else不执行如果一直没找到能整除的数循环正常结束else执行并报告“是素数”。你不需要额外设一个flag变量代码干净多了。2.4 嵌套循环的内外关系记住“外层管行内层管列”嵌套循环就是循环里面套循环最常见的就是打印矩阵、表格、三角形这类二维结构。这时候有个简单的规律外层循环控制行数内层循环控制每一行里的列数。拿打印一个5x5的星号矩形来说for row in range(5): # 外层循环控制5行 for col in range(5): # 内层循环控制每行5个星星 print(*, end) print() # 一行结束之后换行这段代码的执行顺序是外层先取row0然后内层完整跑完5次打印5个星接着print()换行再回到外层取row1如此反复。一定要理解“内层循环是外层循环的循环体”也就是外层每走一步内层就要从头到尾完整跑一遍。如果想让星星变成一个直角三角形只要让内层的终点跟着外层走就行for row in range(1, 6): for col in range(row): print(*, end) print()规律很简单row1时内层range(1)输出1个星row2时内层range(2)输出2个星以此类推。嵌套循环的难点不在于语法而在于你心里有没有“行列坐标”的图景。我建议新手遇到这类题时先别急着写代码在纸上画一个3行的格子标清楚每个空格是外层还是内层负责再动笔写效率会高很多。另外注意break或者continue在内层循环里只会作用于它所在的那一层。比如内层循环里写了break它只会终止内层外层照样继续。这就像是楼里某一层的火警响了只响那一层其他楼层该干嘛干嘛。想一次性跳出多层得靠flag或者封装成函数提前return后面我会专门讲。3. 五个案例把循环彻底玩明白3.1 九九乘法表嵌套循环的标准课九九乘法表是循环入门绕不开的经典题它同时考察了嵌套循环、range边界和格式化输出。模板如下for i in range(1, 10): for j in range(1, i 1): print(f{j}x{i}{i*j}, end\t) print()关键点在于内层range(1, i1)。i从1变到9内层的列数也随之从1增长到9。这样第1行只输出1个式子第2行输出2个符合乘法表的排列。end\t是让式子之间用制表符分隔避免挤成一团。我第一次独立写这个题时把内层写成了range(1, 10)结果每行都输出9个式子整个表变成了长方形。后来才知道内层范围必须依赖外层变量。理解到这一层你也就理解嵌套循环的协同关系了。如果还想增加一点挑战可以把内层循环改成for j in range(1, 10)然后加一个if j i: break效果和上面一样。这两种写法都能跑通但直接用range(1, i1)更清晰简洁。简洁的代码比“炫技”的代码更容易维护。3.2 猜数字游戏while True break的实战组合猜数字可以说是我教别人循环时最喜欢用的案例因为它的交互感强而且把random、while True、break、continue都用上了。规则很简单程序生成一个1到100之间的随机数玩家猜程序提示“大了”或“小了”直到猜中为止。import random target random.randint(1, 100) while True: guess int(input(请输入你猜的数字)) if guess target: print(小了继续) continue if guess target: print(大了继续) continue print(猜对了) break这里的continue让猜错的人直接回到循环开头重新输入break在猜中时彻底退出。你会发现整段代码没有一个复杂的while条件所有退出逻辑都集中在最后的break上非常直白。唯一要提醒的是int(input(...))在玩家输入非数字时会直接报错崩溃所以真实项目里要加try...except但作为循环教学案例先保持干净最好。如果你想增加难度可以加一个猜的次数上限比如超过7次就宣布失败。这正好能练习用变量记录次数并让break出现在两个不同的地方。3.3 素数判断for...else的天然主场判断一个数是不是素数是循环逻辑里很经典的一道题。定义是“大于1且只能被1和它本身整除”。最简单粗暴的办法是从2一直试到n-1看有没有能整除的。但这样太慢了其实只需要试到根号n就够了因为如果n有大于根号n的因子那一定对应一个小于根号n的因子。直接看代码import math def is_prime(n): if n 2: return False for i in range(2, int(math.sqrt(n)) 1): if n % i 0: return False return True这里我用的是return而不是for...else因为在函数里直接return更直截了当。但如果你非要在循环外面用else也可以这么写n 29 for i in range(2, int(n ** 0.5) 1): if n % i 0: print(不是素数) break else: print(是素数)要注意range(2, 2)是空区间也就是说当n很小或者n2、3时循环根本不会进入else直接执行判断结果是素数。这正好符合数学定义。用for...else代码比设flag更清晰也不容易漏掉“循环是否被中断”的状态。我在实际刷题时遇到过大量素数相关题目输出100以内所有素数、找两数之间的素数个数、判断大数素数并优化。这些题目背后的循环骨架都是同一套区别只在于外层套了什么。你只要把“内层试除中断”这个模式刻在脑子里再怎么变形也能接住。3.4 买房子模拟存款、房价、年份一起滚信息学奥赛里有一道“买房子”的题很能说明循环模拟生活的价值。题目大概是这样你年薪N万想买一套初始价格M万的房子房价每年上涨K%按复利计算。你不吃不喝不投资每年年底把所有工资都攒下来问多少年后能买得起这套房如果20年都买不起就输出Impossible。先理清模拟流程第一年你攒下N万拿着这笔钱去看房价。如果存款大于等于当前房价那就买了。如果买不起到第二年年初房价先涨一次你再攒下第二年的N万然后再判断。如此循环。N 50 # 年薪 M 100 # 初始房价 K 10 # 每年涨幅百分比 savings N price M year 1 while savings price and year 20: price price * (1 K / 100) # 房价上涨 savings N # 攒下新一年工资 year 1 if savings price: print(f第{year}年买得起) else: print(Impossible)这个题非常考查循环变量的更新顺序。是“先涨价再攒钱”还是“先攒钱再涨价”直接影响结果。以上面的N50、M100、K10为例第一年结束savings50price100买不起进入循环先涨到110再攒到100year2判断savings price依然成立再循环涨价到121savings变成150year3此时savings150 price121循环退出打印第3年买得起。这类题的通用套路是先明确“一个循环周期里状态怎么变”把存款、房价、年份这三个变量各自的更新公式列出来然后写进循环体。循环的条件往往不是“做几次”而是“存款还小于房价”。这就是while比for更适合的原因。以后你遇到“模拟存款利息”“模拟小球反弹距离”之类的题思路完全一样。3.5 斐波那契数列循环里的递推思维斐波那契数列是另一个循环入门必做题1、1、2、3、5、8、13……每一项都等于前两项之和。很多新手第一反应是用递归但递归在n比较大时非常慢。用循环来递推反而是既直观又高效的解法。n 20 a, b 1, 1 for i in range(3, n 1): a, b b, a b print(f第{n}项是{b})这里用了一个很漂亮的Python特性a, b b, a b可以同时完成赋值。它等价于先计算右边再同时赋给左边。如果拆写成三行就必须引入临时变量否则会算错。循环里的核心思想是前两项已经知道每一项都由前两项推出所以每次循环都把“前两项的窗口”向右移动一格。如果你要生成前n项整个数列可以先用列表存下前两个元素再循环追加fib [1, 1] for i in range(2, 20): fib.append(fib[-1] fib[-2]) print(fib)这种“用列表存结果循环不断追加”的模式在数据处理里太常见了。比如你要统计每月的销售额累计值、计算每天的移动平均都是同一个套路把上一次的结果留存在一个变量或列表里下一轮接着用。4. 循环优化与翻车现场老手也躲不掉的坑4.1 边遍历边改列表结果为什么不对我在刚工作那会儿犯过一个经典错误想用一个for循环把列表里的偶数全部删除结果删完之后列表里还残留着一堆偶数。原因很简单遍历列表时Python的内部索引是按顺序往后走的但一旦删除了元素后面的元素会往前补位索引就错位了。举个例子nums [1, 2, 3, 4, 5, 6] for n in nums: if n % 2 0: nums.remove(n) print(nums) # 你可能以为会得到 [1,3,5]实际是 [1,3,5]其实不一定别急着以为它正确。如果列表是[1, 2, 4, 5, 6]你在删除2之后索引继续往后走指向原来的4但4已经补到了2的位置于是被跳过了最终结果就会出问题。这个错误非常隐蔽没踩过坑的人很难一眼看出来。解决办法也简单要么遍历原列表的副本要么用一个新列表收集要保留的元素nums [1, 2, 3, 4, 5, 6] nums [n for n in nums if n % 2 ! 0] # 列表推导式最简单 print(nums)如果一定要在原列表上操作也可以倒着遍历。因为从尾部删除元素不会影响前面元素的索引nums [1, 2, 3, 4, 5, 6] for i in range(len(nums) - 1, -1, -1): if nums[i] % 2 0: del nums[i] print(nums)记住一个原则不要在一个循环里同时遍历和修改同一个容器的长度。要么用副本要么用推导式要么倒序删。4.2 不是所有循环都硬写这些写法更快更省心有些开发者一看到“问题里有一串数字要处理”脑子里第一反应就是写for循环。可Python里很多内置函数和语法糖能让代码更短、性能更好。能用一句话表达的逻辑就别写三行循环。比如要把一个列表里所有数字平方新手可能写squares [] for x in range(10): squares.append(x * x)用列表推导式就是一行squares [x * x for x in range(10)]不光是写法简洁推导式在CPython内部会有专门的优化通常比手动append循环更快。同理sum([1, 2, 3])直接求和max([1, 5, 2])直接取最大all和any用于判断全部或任一满足条件这些都能替代一堆循环逻辑。如果需要同时遍历两个列表不要用for i in range(len(list1))再去list1[i]、list2[i]直接用zipnames [张三, 李四, 王五] scores [88, 95, 72] for name, score in zip(names, scores): print(name, score)如果需要带索引遍历用enumeratefor idx, value in enumerate([a, b, c], start1): print(idx, value)这些都是“循环的好朋友”它们的作用就是让你少写半屏代码。不过也要提醒一句推导式虽然简洁但嵌套多层之后会变得极难读比如[[x*y for x in range(5)] for y in range(5)]这种一眼看不懂的代码尽量拆开写不然维护成本比多写几行循环高得多。4.3 循环常见问题速查表遇到问题直接对号入座我把自己以及身边同事在循环上踩过的一些高频坑整理成了表格你可以遇到问题直接查。症状可能原因解决办法程序运行很久不结束while条件永远为真循环体没有更新条件变量检查循环体末尾是否有变量递增或状态变化必要时打印条件值遍历列表时漏掉元素循环中删除元素导致索引前移跳过遍历副本、用推导式或倒序删除IndexError: list index out of range使用下标访问时索引超过了列表长度改用for item in list或限制索引范围循环内修改了外部变量但外部没变对不可变对象如int重新赋值不会改到外部需要把结果通过返回值带回外部或使用可变对象break只退出了一层嵌套循环中break只作用于当前层使用flag标记或者把循环封装成函数用return退出循环变量在循环结束后仍可用for循环变量在结束后保留最后值需要时重新初始化注意作用域range(1,10)少了一个数忘了range是左闭右开区间确认起始值是否需要1用list(range(...))快速验证continue下方的代码一直不执行continue跳过了本轮剩余语句检查continue是否放得太早确认逻辑正确这张表里的每一条背后都有真实的项目事故。尤其“break只退出一层”这个问题我在代码评审时见过不知道多少次。新手写完嵌套循环发现结果不对怎么查都查不出最后才发现break在内层。所以遇到嵌套循环要提前想清楚你到底想退到哪一层如果只想退出当前内层break没问题如果想直接退出所有层最好把这段逻辑放进函数用return一下子结束。5. 我写循环的一些私房经验送给你当参考5.1 把循环体拎出来变成函数思路立刻清爽有时候一个循环里的操作特别多要先算这个再判断那个中间还要更新三个变量。这种循环写到最后又长又乱一个不小心逻辑就错了。我的习惯是把循环体里比较复杂的那部分抽出来单独写成一个小函数然后在循环里调用它。比如你有一段循环要对一系列订单计算折扣后的金额还要判断订单是否满足包邮条件。我不会把几百行都塞进循环里而是会这样组织def calc_discount(order_amount): if order_amount 500: return order_amount * 0.8 if order_amount 200: return order_amount * 0.9 return order_amount orders [128, 256, 680, 99] for amount in orders: final_price calc_discount(amount) print(amount, final_price)这样循环本身只剩下三行主逻辑一眼就能看懂。以后如果折扣规则变了你只需要改calc_discount函数根本不用去碰循环。这种“把循环体函数化”的习惯能让代码的可读性和可维护性上一个台阶。5.2 列表推导式是糖衣但别贪吃我前面提过列表推导式很好用但这里要泼一盆冷水它不适合所有场景。简单的一层推导式比如[x**2 for x in range(10)]可以直接用。但如果你的推导式里出现了多个for、多层if甚至还有三元表达式那读起来就像天书了。我给自己定了一个规矩推导式超过“一个for一个if”就不再硬写直接用普通循环加注释。因为代码是给人和机器一起看的机器不在乎难不难读人在乎。你写一段别人要揣摩三分钟的推导式还不如老老实实写个循环让下一个维护者少掉几根头发。生成器表达式是另一个值得提的东西。它和列表推导式很像但外层是圆括号不会一次性把所有结果存到内存total sum(x * x for x in range(1000000))这个写法在数据量很大时特别合适内存占用低性能也好。它本质上还是一个循环但把循环的“结果”变成了可迭代对象适合用在内置函数sum、max、min里。5.3 调试循环的土办法比print好用多了循环出错时第一反应当然是print。但我见过很多人print之后不舍得删结果代码里到处都是调试输出。我的建议是调试好之后就删干净或者临时用一个DEBUG开关控制。最有效的调试方法之一是把循环中每一轮的“关键变量变化”打印出来。比如模拟买房那个例子你可以打印每年的存款和房价一眼就能看出是哪一步更新顺序错了year 1 savings N price M while savings price and year 20: price price * (1 K / 100) savings N year 1 print(f第{year}年: 存款{savings}, 房价{price})这样运行一次所有变量变化都摆在眼前比在脑子里干想要靠谱得多。还有一种技巧是测试边界值如果循环条件里有就试一下等于的情况下对不对如果循环要从0开始试一下n0会不会崩溃如果range结束值是10试一下9和10两个临界点。很多隐藏bug都藏在边界里。如果循环体比较复杂还可以用Python自带的pdb调试器。在循环体里插入breakpoint()程序执行到这里会进入交互式调试模式你可以输入变量名查看当前值用n执行下一行用c继续跑完。这个工具比print强大得多尤其在处理复杂嵌套循环时能省下大把猜的时间。最后再分享一个小技巧写循环之前先想清楚“每个循环周期结束后哪些变量应该变成什么”。把这个状态变化写在纸上或者写成注释再开始写代码。我用了这个习惯之后循环相关的bug明显少了。你也不需要每次都用但碰上那种需要更新多个变量的模拟类题目这个小步骤真的能救命。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。