
1. 内容整体设计与思路拆解1.1 为什么你绕不开循环结构我刚带完一个 Python 零基础训练营发现大部分新手的第一个瓶颈不是变量、不是数据类型而是循环结构。原因很简单循环打破了代码“从上到下逐行执行”的直觉让同一段逻辑反复运行这种思维的转变需要一个适应期。其实 Python 的循环结构就两兄弟for和while。它们做的事情本质上一样——让一段代码重复执行。差别在于for循环适合“我知道要循环多少次”的场景比如遍历一个列表、处理一个字符串while循环适合“我不知道要循环多少次但我知道什么时候该停”的场景比如不断读取用户输入直到输入正确、持续轮询某个状态直到变化。换句话说for循环管的是“次数”while循环管的是“条件”。这个区分你搞清楚之后后续写任何循环代码都不会迷糊。1.2 本文会带你怎么学这篇指南我会按“理解原理 → 掌握细节 → 动手实战 → 解决问题”的顺序来拆。首先带你弄清楚 Python 循环结构的底层逻辑然后逐行解析for和while的语法细节再通过两个实战案例——数字累加和凯撒密码加密——把循环的知识点串起来最后整理一份我在实际教学和开发中遇到的常见问题排查表。如果你是刚装好 Python、还在找编辑器的新手建议先花点时间把环境准备好。我的经验是直接用 VS Code 加 Python 插件装完就能跑不用折腾。VSCode 安装包自带 Python 扩展在扩展商店搜“Python”装第一个就行。环境弄好之后把这篇指南里的代码逐行敲一遍比只看不练效率高十倍。2. 核心细节解析与实操要点2.1 for 循环先搞清楚它在遍历什么for循环的语法结构是for 变量 in 可迭代对象: 循环体语句关键点在于“可迭代对象”。在 Python 里字符串、列表、元组、字典、集合、range()生成的对象都是可迭代的。这意味着你写for 名字 in 名单列表其实是在按顺序把列表里的每个元素取出来赋给变量名字然后执行一遍循环体。这里有一个很多新手会犯的直觉性错误以为for循环必须配合数字下标使用。其实在 Python 里完全不需要直接遍历元素本身就行。比如names [小明, 小红, 小刚] for name in names: print(name)这段代码会依次打印三个人名和下标没有半毛钱关系。如果你确实需要知道当前是第几个元素用enumerate()函数names [小明, 小红, 小刚] for index, name in enumerate(names): print(index, name)输出结果是0 小明、1 小红、2 小刚。enumerate()返回的是“下标-元素”的配对直接解包成两个变量接收就行简洁又直观。2.2 range() 函数循环次数控制的灵魂真正的循环次数控制靠的是range()函数。它生成一个等差数列常见的三种写法range(stop)生成从 0 到 stop-1 的整数序列range(start, stop)生成从 start 到 stop-1 的整数序列range(start, stop, step)按 step 步长生成整数序列踩坑提醒range()是不包含结尾的也就是“左闭右开”。想生成 1 到 100你得写range(1, 101)不是range(1, 100)。这个“差一”的问题几乎每个初学者都会踩我在教学中反复强调也没用还是得自己写错一次才能记住。步长参数step很好用。想打印所有偶数只需要range(0, 101, 2)想倒着数用负步长range(10, 0, -1)这时候注意 start 要大于 stop。2.3 while 循环条件满足就继续while循环的语法更简单只有一个判断条件while 条件表达式: 循环体语句每次执行循环体之前先判断条件条件为True就执行为False就跳出。这种结构非常适合“循环次数不确定”的场景。举个例子写一个用户登录验证的程序要求用户输入密码直到正确为止password 123456 user_input while user_input ! password: user_input input(请输入密码) print(登录成功)这段代码会一直让用户输入密码输对了才往下走。循环次数完全由用户的操作决定程序员事先无法预算这就是while的典型使用场景。但这里有一个非常恐怖的陷阱——死循环。如果条件永远为True程序就会永远跑下去。比如while True: print(停不下来了)while True这种写法本身没有错很多服务器程序就是靠它保持监听状态的。关键是你必须在循环体里放一个break语句作为退出的出口。后面我会详细讲break和continue的配合技巧。2.4 break、continue 和 else循环的控制流三件套只掌握for和while的语法循环结构只能算学会了一半。真正让循环结构变得灵活的是break、continue和else三个关键字。break的作用是立即结束整个循环不管循环条件是否还满足。它常用在“提前退出”的场景。比如搜索一个列表里是否存在某个元素找到了就提前收工nums [1, 3, 5, 7, 9] target 7 for num in nums: if num target: print(找到了) breakcontinue的作用是跳过本次循环的剩余部分直接进入下一次循环。它适合用来过滤不需要处理的数据。比如只处理列表中大于 0 的数字data [1, -2, 3, -4, 5] for num in data: if num 0: continue print(num)else在循环中比较冷门但非常好用。它的逻辑是如果循环正常结束没有被break中断就执行else子句。经典的用途是“查找失败”的判断nums [2, 4, 6, 8] target 5 for num in nums: if num target: print(找到了) break else: print(没找到)这里else块只会在循环完整跑完、没有触发break的情况下执行。如果你用其他语言工作过可能会觉得这个设计很怪但记住“else就是break的陪跑员”就能理解其工作方式了。3. 实操过程与核心环节实现3.1 实战案例一用循环完成 1 到 100 的累加光说不练假把式我们动手写点有实际意义的代码。第一个案例是经典中的经典求 1 加到 100 的和。用for循环实现total 0 for i in range(1, 101): total i print(total)运行结果输出 5050。逻辑拆解一下先定义变量total存放累加结果初始为 0然后让i从 1 遍历到 100注意range(1, 101)的结尾不包含每次循环把当前的i加到total上全部结束后打印结果。用while循环实现total 0 i 1 while i 100: total i i 1 print(total)这里手动维护了一个计数器i每次循环让i加 1直到i超过 100 跳出循环。对比两种写法for版本简洁不少不需要手动维护计数器的递增逻辑出错的概率也低一些。这也是为什么我建议能确定循环次数的时候优先用for。3.2 实战案例二用 while 循环实现凯撒密码加密热搜里出现了一个很有意思的需求用 while 循环实现凯撒密码加密。这是一个非常适合练手的实战项目因为它需要把循环结构、字符串处理、条件判断、字符编码转换全部串联起来。凯撒密码的原理很简单把每个字母按照固定的位移量替换成另一个字母。比如位移量是 3那么a变成db变成ex变成a以此类推。这是古罗马时期就有的加密方式听起来古老但用来练习循环结构恰到好处。先看完整的实现代码def caesar_cipher(text, shift): result for char in text: if char.isalpha(): ascii_base ord(a) if char.islower() else ord(A) new_pos (ord(char) - ascii_base shift) % 26 new_char chr(ascii_base new_pos) result new_char else: result char return result plain_text input(请输入要加密的文本) shift 3 encrypted caesar_cipher(plain_text, shift) print(加密结果, encrypted)代码中处理了一个关键问题字母表是一个环形结构z偏移 3 位要绕回c。这里用取模运算% 26实现绕回逻辑意思是把字母转换成 0 到 25 的数字偏移后再取模最后转换回字符。再来看用while循环实现的版本顺便练习循环嵌套——外层用 while 控制文本遍历内层可以用 while 处理单个字符的偏移逻辑def caesar_cipher_while(text, shift): result index 0 while index len(text): char text[index] if char.isalpha(): ascii_base ord(a) if char.islower() else ord(A) offset (ord(char) - ascii_base shift) % 26 new_char chr(ascii_base offset) result new_char else: result char index 1 return result重点看while版本里的计数器index。index从 0 开始每处理完一个字符就自增 1当index等于字符串长度时循环结束。如果不写index 1程序就会死循环因为字符串长度不变、条件永远成立、index永远不增长。这个真实案例能让你直观体会到循环结构自我管理的核心——每一次循环都要向退出条件靠近一步。解密则是加密的逆运算只需要把位移量改成负数decrypted caesar_cipher_while(encrypted, -3) print(解密结果, decrypted)做这个案例时你还会接触到两个内置函数ord()和chr()。ord()把字符转换成 Unicode 码点chr()把码点转换回字符。用一个例子说明ord(a)返回 97chr(97)返回a。3.3 实战案例三循环嵌套——九九乘法表与二维结构循环还能相互嵌套使用外层循环每执行一次内层循环要完整执行一轮。最经典的例子是打印九九乘法表for i in range(1, 10): for j in range(1, i 1): print(f{j}×{i}{i*j}, end\t) print()这段代码的行逻辑是外层循环控制行数i从 1 到 9内层循环控制每行输出几个算式j从 1 到i。end\t让每个算式后面跟一个制表符而不是换行内层循环结束后用print()换行。嵌套循环的难点在于理解执行顺序。你只要记住一个原则外层循环跑一轮内层循环跑满全程。第一次外层i1时内层只输出1×11一个式子然后换行第二次外层i2时内层输出1×22和2×24两个式子然后换行。逐层递进最终形成一个左下角的三角形布局。如果你需要遍历二维数据嵌套循环几乎是标配。比如一个三行四列的矩阵求和matrix [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ] total 0 for row in matrix: for num in row: total num print(total)外层遍历行内层遍历该行的每一个元素逻辑清晰、可读性高。3.4 循环与列表推导式的取舍写完嵌套循环后我想特意提一下 Python 的特色语法——列表推导式。很多场景可以用一行代替整个循环。最简单的例子生成 0 到 9 的平方列表squares [] for i in range(10): squares.append(i * i)用列表推导式就是squares [i * i for i in range(10)]代码量少了三分之二。再复杂一点只保留偶数的平方squares [i * i for i in range(20) if i % 2 0]这并不是说列表推导式要取代普通循环而是建议你理解它是一种语法糖本质上还是循环结构在背后工作。什么时候用推导式当表达式逻辑简单、一目了然时。什么时候回归普通循环当循环体逻辑复杂、需要多行处理时。判断标准只有一条代码是否清晰可读。强行把一个复杂逻辑塞进推导式反而得不偿失。4. 常见问题与排查技巧实录4.1 死循环程序卡住不走了怎么办死循环是最常见也最让人头大的问题。典型症状是程序运行后没有任何输出或一直重复输出根本不结束。我在教学过程中无数次看到学员的程序卡死排查思路通常按以下顺序检查循环条件是否最终会变成 False。如果是while True有没有break出口检查循环体内是否更新了条件对应的变量。比如while i 100的循环里有没有写i 1确认 break 是否被放在合适的嵌套层级中。break只结束它所在的这一层循环如果你写的是嵌套循环它不会结束外层循环。比如这样一个经典错误i 0 while i 5: print(i)这段代码会无限打印 0因为i的值从未变化条件i 5永远为真。加上i 1才能正常退出。遇到死循环先不要慌在终端里按CtrlC可以强制中断运行然后审查代码找问题。4.2 循环中修改迭代对象经典踩坑在遍历列表的同时删除列表元素大概率得到意外结果nums [1, 2, 3, 4, 5] for num in nums: if num % 2 0: nums.remove(num) print(nums)输出结果是[1, 3, 5]看起来好像没问题但换成奇数移除时结果就不对劲了。原因是列表在遍历时内部索引一直在变化删除元素会导致后面的元素前移漏掉一些元素。一个可靠的解决办法是遍历原列表的副本操作原列表nums [1, 2, 3, 4, 5] for num in nums[:]: if num % 2 0: nums.remove(num)nums[:]创建了一个原列表的复制品遍历期间用它来迭代删除操作作用于原列表就不会互相干扰。或者干脆用列表推导式生成一个新列表nums [num for num in nums if num % 2 ! 0]简洁高效我日常写代码更推荐这种方式。4.3 边界问题range 的结束值为什么总是差一个range(1, 10)生成的是 1 到 9没有 10。这个设计在很多编程语言中都存在称之为“半开区间”。好处是可以直接计算出长度range(start, stop)的长度就是stop - start当步长为正时。边界问题的表现形式通常有两种。一种是循环次数比预期少一次比如想循环 5 次却写了range(5)实际循环 0 到 4 共 5 次注意这是对的关键是你要理解range(5)的 5 代表“数量”而不是“最大值”。另一种是大遍历末尾的一个元素被漏掉了比如判断质数时只判断到sqrt(n)之前。我的排查建议是不确定边界时加一行打印调试for i in range(1, 101): print(i, end )清空控制台后跑一下看一下首尾值是否符合预期一目了然。4.4 循环性能大循环卡顿的常见原因与优化方向循环写多了你会发现有些循环跑得特别慢。常见原因有不必要的大量字符串拼接。在循环里反复执行result char会频繁创建新的字符串对象。数据量小无所谓上万次循环时性能差距就明显了。更好的做法是收集到列表里最后用.join()一次性拼接。不必要的重复计算。比如在循环体内重复求列表长度、重复调用某函数如果这个值和循环无关就应该提到循环外面。用while硬扛大范围遍历。用纯 Python 循环处理上亿级别数字的效率非常低这时候需要考虑 NumPy 这类库或者借助itertools等高效迭代工具。举个简单的优化示例# 低效写法 chars [] for c in text: if c.isalpha(): chars.append(c.upper()) result for c in chars: result c # 高效写法 result .join(c.upper() for c in text if c.isalpha())第二种写法不仅代码更短内部用生成器逐步求值还避免了大量临时字符串对象的创建内存占用和运行时间都能明显下降。日常开发中写出正确的循环是第一优先级但性能敏感场景要注意避开这几个坑。5. 容易被忽略的进阶知识点5.1 循环变量的值泄漏问题在 Python 中for循环的循环变量在循环结束后仍然保留最后一个值不像有些语言会将其限定在循环局部作用域中。看这个例子for i in range(5): pass print(i)输出结果是 4。这种设计在写后续代码时可能造成干扰。比如你已经有一个变量叫i后面又在某个循环里用了它循环结束后i的值就会被覆盖。如果下面的代码还依赖原来的i就会产生逻辑错误。规避方式很简单给循环变量取一个语义明确的名字比如index、num、item不要用i、j到处都是这样既能避免干扰也能提升代码可读性。5.2 循环柄与迭代器理解 for 背后的迭代协议for循环看起来简单身后的逻辑其实很优雅。它先调用iter()函数获取一个迭代器对象然后反复调用next()获取下一个元素直到抛出StopIteration异常才退出循环。你可以手动模拟这个过程nums [10, 20, 30] it iter(nums) while True: try: num next(it) print(num) except StopIteration: break这个原理的好处之一是可以理解为什么不建议在循环过程中修改迭代对象因为迭代器持有的是对象的指针和当前位置。更妙的是理解了迭代协议之后你可以自定义可迭代对象用于自己的数据结构。简单实现可以让类提供__iter__和__next__方法这样你的对象就能被for遍历了。这是 Python 循环结构从“语法”走向“设计模式”的一步。5.3 循环中的异常处理循环体内部如果有异常风险比如类型转换或文件读取通常用try包住但要注意异常处理在循环中的位置会决定程序的整体行为。比如while True: try: num int(input(请输入一个整数)) break except ValueError: print(输入无效请重新输入)这个循环会一直要求用户输入直到得到一个合法的整数才跳出。如果把try挪到循环外面程序遇到非法输入就会直接崩溃。循环加异常处理是构建健壮交互程序的基础。但有经验的开发者会提醒你不要在循环体内捕获过度宽泛的异常比如except Exception这样很容易掩盖原本可以修复的逻辑错误。务实的做法是捕获预期内、可恢复的具体异常类型其他异常宁可让程序报错也不要静默吞掉。6. 最后的几个小建议我在日常带新人时经常强调一句话写循环时先从退出条件开始想再写循环体。很多人一上来就闷头写循环体结果写完发现自己根本不知道循环什么时候结束回头补条件时又把逻辑弄乱了。先问自己三个问题循环什么时候结束每次循环要接近结束条件多少如果中途要退出哪个分支该break把这三个问题想清楚循环结构基本就不会写错。另外建议你在本地搭一个练习环境。具体操作是装好 Python 后在 VS Code 里新建一个.py文件配置好 Python 解释器然后把我写的这几个案例一个个手动敲一遍。不要复制粘贴手动敲的效果好得多。敲完再试试改改参数、换个场景比如把凯撒密码的位移量改成可输入的、把累加范围改成 1 到 n 的任意值这样循环结构才算真正吃透了。如果你按这个思路完成了练习循环相关的知识点应该已经掌握得很扎实了。后面学习列表推导式、生成器、递归时你会发现它们本质上都在和“重复执行”打交道那时候回头再看循环结构一切都顺理成章。