ARTICLE DETAIL

资讯详情

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

Python循环4种核心用法拆解:for、while、嵌套与控制流实战

Python循环4种核心用法拆解:for、while、嵌套与控制流实战 写Python这几年我发现自己和大多数人一样每天写来写去高频的代码其实就那几样列表推导、字典取值、条件判断剩下的就是循环。Python循环这个东西说简单是真的简单for i in range(10)谁都能写但说复杂也是真的复杂嵌套一深加上break、continue、else混在一起再碰上一次死循环新手基本靠猜。今天这篇就把Python循环的4种核心用法完整拆开从最基础的for到控制流再到真实项目里的循环实战思路尽量把每段代码背后的选择逻辑也讲清楚。适合刚入门想系统搞懂循环的人也适合写过一段时间、但总觉得自己在“背写法”的读者。看完你会发现循环不是语法点而是一套解决问题的思维方式。1. 循环的本质先搞懂这段代码重复了什么很多人学循环是一上来就背for i in range(100)背完了遇到实际问题还是不会用。问题不在语法而是没想清楚“循环到底在解决什么”。所以我习惯先讲本质再讲写法。1.1 循环只是“重复”吗它解决的其实是三类问题循环解决的第一类问题是重复劳动。比如有100个文件要改名、1000条数据要清洗、一整个列表的元素要逐个打印人肉重复干这些事既不现实也容易出错。机器就没有这个问题只要给它一套固定动作它可以循环到天荒地老。第二类问题是批量数据的处理。数据分析里最常见的就是读多个文件、拼多个接口返回、批量入库。这类需求本质上都是对一批同构的数据做同样的操作而循环就是表达“对每个元素做这件事”的最直接工具。第三类是迭代计算。累加求和、算阶乘、模拟小球反弹、做数值逼近、写游戏主循环这类问题的特点是当前结果依赖上一次的结果需要利用循环反复更新状态。你在热词里看到的小球落地反弹、循环控制案例本质都属于这一类。一句话总结只要你想让代码“重复做一件事并且每次都可能基于上一次的结果”这就是循环的适用场景。搞清楚自己要解决的是哪类问题后面选哪种循环写法就是顺理成章的事。1.2 for还是while先看循环次数是否确定Python里就两种循环for和while。很多人纠结怎么选其实标准特别简单循环次数已知或者可以枚举用for循环次数未知只能靠某个条件来终止用while。判断维度for循环while循环适用场景遍历列表、元组、字典、字符串或配合range做固定次数循环用户输入校验、读取文件直到结尾、事件驱动轮询循环次数事先明确事先不明确取决于条件何时为False风险点相对可控基本不会死循环忘记更新条件变量就容易死循环典型口诀“对这批数据里的每一项做处理”“只要条件成立就一直做直到不成立为止”我举个对比非常直观的例子。遍历一个列表你当然知道列表长度这时候用for就是最自然的。但如果你要设计一个“让用户输入密码直到输入正确为止”的功能你根本不知道用户会输错几次for就没法用了只能用while。记住这个区分逻辑选型就不会错。1.3 动手写循环前先在脑子里过一遍三条退出路径我写循环之前习惯先在脑子里把整个过程走一遍重点不是走“正常执行”那条路而是想清楚三条可能的退出路径循环正常跑完、中途用break退出、本次continue跳过剩余操作。想清楚了循环才不会失控。很多新手写循环容易“只见树木不见森林”写for i in range(50)就只知道会跑50次但遇到第10次该跳过去、第30次该提前结束的需求就不知道往哪里放了。其实循环里有三种跳转逻辑提前想好写代码的时候就不会东补一块西补一块。这个小习惯看起来不起眼但在后面处理嵌套循环和for...else的时候会帮你省很多调试时间。2. 逐个拆解Python循环的4种核心写法基础概念讲完了下面进正题。我按实际使用频率和重要程度把这4种写法逐一拆开讲for加range、while、嵌套循环、循环控制流break/continue/for...else。从用法、适用场景到易错点一次说透。2.1 第一种for加range批量遍历的默认主力for循环是Python里用的最多的循环而它最常见的搭档就是range。先看range到底怎么用# 生成[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for i in range(10): print(i) # 生成[1, 3, 5, 7, 9]起始为1结束于10不含步长为2 for i in range(1, 10, 2): print(i) # 反向遍历[5, 4, 3, 2, 1] for i in range(5, 0, -1): print(i)这里有个很多人刚学时困惑的点为什么range(10)不包含10我的理解是range用的是标准的“左闭右开”区间也就是[start, stop)。这样设计有个好处range(len(lst))拿到的索引和列表的下标天然对应最后一个索引总是len(lst) - 1不会越界。你写for i in range(len(lst))时i最大正好是最后一个元素的下标天然安全。实际工作中for加range最常见的使用场景是翻页遍历。我举个例子写爬虫的时候经常要遍历多页列表base_url https://example.com/list page_size 20 for page in range(1, 6): # 抓取前5页 url f{base_url}?page{page}size{page_size} # 这里发起请求解析数据 print(f正在处理第{page}页: {url})这种写法好在逻辑一目了然页码从1到5边界清晰不会出现多抓一页或少抓一页的情况。我个人觉得range就是Python里最实用的“计数器生成器”不管你是遍历索引、生成页码范围、还是控制循环次数它都是第一个该想到的工具。2.2 第二种while循环次数未知时的保险方案while的运行逻辑一句话就能说明白只要条件为真就一直执行循环体。它的结构比for更简单但也正因为简单写起来更容易翻车。来看一个典型场景——用户输入校验password while len(password) 6: password input(请输入至少6位的密码: ) print(密码设置成功!)这个循环条件是len(password) 6只要用户输入的密码长度不满足要求就会一直弹窗让你重新输入直到满足为止。这种需求没法用for写因为你根本不知道用户会输错几次。这就是while的不可替代性。另一个常见场景是游戏主循环比如一个极简的猜数字游戏import random target random.randint(1, 100) guess 0 attempts 0 while guess ! target: guess int(input(猜一个1到100之间的数字: )) attempts 1 if guess target: print(小了再猜!) elif guess target: print(大了再猜!) print(f恭喜你猜了{attempts}次就中了!)这里while负责“不知道循环几次”的交互逻辑每次用户输入后更新判断条件直到命中目标为止。我在实际工作里用while最多的场景反而是轮询任务状态比如循环检查后台任务是否完成没完成就等待几秒再查完成了就退出。这类逻辑用while写非常顺手。写while必须养成一个好习惯循环体内一定要有能够改变条件的语句。上面例子里的attempts 1、guess int(input(...))就是改变条件的关键。如果你忘记这步条件永远成立就成了死循环。我自己的规矩是写完while先检查三件事——条件变量是否初始化了循环体内是否更新了它有没有可能出现永远不为False的情况2.3 第三种嵌套循环二维数据批量处理嵌套循环就是循环里面套循环最典型的场景是处理二维结构比如表格、矩阵、棋盘、九九乘法表。它的规律可以记成一句话外层循环管“行”内层循环管“列”。来看最经典的九九乘法表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, i 1)让列数不超过行数正好形成三角形end\t把末尾的换行改成制表符让每一行内部可以连续输出多个算式最后那个空print()负责在一行结束后换行。如果你把end参数删掉或者把换行位置搞错输出的九十九乘法表就全乱了。嵌套循环也可以用来遍历二维列表这在数据处理里很常见matrix [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] for row in matrix: for col in row: print(col, end ) print()这段代码做什么呢外层循环拿到的是每一行一个子列表内层循环拿到的是子列表里的每一个数字。稍加改造你就能统计总和、找最大值、做矩阵转置都能用同一套骨架改出来。这里要特别提醒一个性能相关内容嵌套循环的总执行次数是外层次数乘以内层次数。如果外层100次、内层100次总共就是10000次大多数场景没问题但如果外层10000次、内层10000次那就是一亿次程序就会肉眼可见地变慢。所以写嵌套循环之前先估算一下数据规模如果量级太高就得考虑用numpy、pandas这种底层优化的工具替代或者减少循环层数。2.4 第四种break、continue和for...else循环里的控制流循环不只是闷头跑完它还提供了三个控制工具break用来提前结束整个循环continue用来跳过本次循环的剩余代码直接进入下一次for...else用来在“循环正常结束”时执行一段逻辑。break的典型场景是在循环里找到目标就停。比如在一个列表里找第一个大于100的数nums [23, 55, 88, 120, 77, 200] for n in nums: if n 100: print(f找到了: {n}) break一旦找到120break立刻终止循环后面的77、200都不会再遍历了。这种“短路式”写法在性能上有明显优势尤其是数据量很大的时候没必要把整个列表全跑一遍。continue则相反它不结束循环只是跳过这一次。最常见的用途是清洗脏数据循环里遇到空值或非法值就跳过不处理不报错继续下一个。raw_data [正常, , None, 有效, 待处理] for item in raw_data: if not item: continue # 跳过空字符串和None print(f处理: {item})这个写法在数据预处理脚本里特别实用你只需要用continue把不合条件的过滤掉剩下的就是主流程。注意continue要放在判断条件里别放在循环体最后一行否则它跟没写一样。for...else是很多人没用过的特性它的行为是循环正常结束没被break中断就执行else里的代码如果循环是被break中断的else不执行。最经典的应用是判断素数def is_prime(num): if num 2: return False for i in range(2, int(num ** 0.5) 1): if num % i 0: print(f{num}不是素数能被{i}整除) break else: print(f{num}是素数) return True return False is_prime(17) is_prime(21)这段逻辑如果不用for...else就得额外搞一个flag变量来标记“是否被整除过”写起来啰嗦得多。用for...else的话break和else正好形成互斥关系——只要发生了breakelse就一定跳过只要循环完整跑完说明没被整除那就是素数。这个写法第一眼可能不习惯但它确实是Python里很有风格的控制方式用熟以后写这类“循环内搜索搜索不到做兜底”的需求很顺。3. 真实项目中循环怎么用三个可复制的案例光会写语法还不够最重要的是能拿循环解决实际题目和业务需求。我挑三个不同层次的案例从经典题目到真实批量处理场景把循环的运用过程完整走一遍。3.1 信奥经典题“买房子”循环累加和条件判断的标准模板这道题在编程基础题里非常有名一个人年薪固定为N万元每年年初发到手后就攒起来不吃不喝全存着房子初始价格200万元每年价格涨幅固定为K%。问第几年他能买得起这套房如果存钱速度永远赶不上房价输出No。这类题的思路是模拟每一年的“存款增加”和“房价上涨”用循环逐年推演直到存够钱或者确认无望N 10 # 年薪10万 K 10 # 房价每年涨10% price 200 # 房价初始值200万 savings 0 # 当前存款 for year in range(1, 70): # 最多模拟69年 savings N # 本年工资到账 if savings price: print(f第{year}年能买房存款{savings:.2f}万房价{price:.2f}万) break price * (1 K / 100) # 房价上涨 else: print(No永远买不起)这里值得学的点有三处。第一用for配合range控制最大模拟年数防止万一出现意外导致死循环第二循环体内先判断再更新状态顺序非常关键——如果先涨价再判断结果就错了一年第三for...else在这里又派上用场了如果69年都买不起说明工资涨幅追不上房价直接落入else输出No。这个例子把循环、条件判断、break、else全串起来了建议亲手敲一遍。3.2 小球反弹模拟用循环把物理过程变成代码还有一道很经典的循环题——小球从100米高度自由落下每次落地后反弹到原高度的一半再落下求第10次落地时总共经过多少米以及第10次反弹的高度。这题看起来是物理问题实际上是一个非常好的循环建模练习height 100 # 初始高度 total_distance 100 # 第一次落下经过的距离 for i in range(9): # 注意只需要模拟第2次到第10次落地 height / 2 # 反弹高度变为原来一半 total_distance height * 2 # 这段包含“弹起落下”两段路程 print(f第10次落地时总路程: {total_distance:.2f} 米) print(f第10次反弹高度: {height / 2:.2f} 米)这个题最坑的地方就是循环次数。第一次落下的100米已经计入总路程所以后面只需要循环9次而不是10次。如果你在循环里一上来就累加height * 2就会把第一次落地的路程重复算一次。这种“边界次数”问题在循环里天天能遇到我的经验是先手动模拟两三轮把循环次数和变量更新时机理清楚了再写代码比先写代码再调试快得多。3.3 批量读取数据文件数据分析里最常见的循环场景现实业务里的循环没有“买房子”那么清爽常常要处理一大堆文件、一堆接口。我就以数据分析中批量读取CSV文件为例展示循环如何和第三方库配合import glob import pandas as pd all_data [] # glob匹配data目录下所有csv文件 for file_path in glob.glob(data/*.csv): df pd.read_csv(file_path) all_data.append(df) print(f已读取 {file_path}共 {len(df)} 行) # 合并所有数据 merged_data pd.concat(all_data, ignore_indexTrue) print(f合并完成总行数: {len(merged_data)})这个场景里glob.glob先按模式匹配出所有路径然后for循环逐个读取append到一个列表里最后用concat拼接。这里有几个经验分享一是尽量别在循环里写重量级操作。比如循环内打开数据库连接每读一个文件就连接一次性能会很难看。如果文件数量多应该改为循环外只连接一次循环内复用连接。二是给循环体加一点“日志感”。处理量大的时候在关键节点打印进度既能确认循环确实在执行也能快速定位到底卡在第几个文件。我跑批量任务常年保留这一行print。三是学会用列表推导式优化简单循环。比如上面的df汇总本质是“对每个元素做一件事并收集结果”可以写成更紧凑的形式。但可读性优先新手阶段老老实实写for也没问题等熟练了再逐步过渡到推导式。关于翻页爬虫我在前面2.1节已经给过页码拼接的例子本质就是for配合range生成URL列表。如果你处理的列表特别大还可以考虑用enumerate同时拿索引和元素files [a.csv, b.csv, c.csv] for idx, file in enumerate(files, start1): print(f正在处理第{idx}个文件: {file})这样就不用额外定义一个计数器了。enumerate是我在真实项目里用得最频繁的迭代辅助函数之一简单但极其实用。4. 循环常见问题与排查经验最后这部分是重点中的重点全部来自我自己写代码、帮别人改代码时遇到的真实坑。循环的语法就那么几个真正拉开差距的是能不能快速定位和解决循环中的异常行为。4.1 死循环最让人头皮发麻的问题死循环是不可忽视的问题我用while写轮询的时候至少经历过三次程序卡死的状况。排查思路其实可以总结成一个三步检查法第一步检查循环条件是否有机会变成False。比如while count 10问自己一句count到底有没有在循环体里增加如果循环体里没有让条件变化的语句这就是死循环的最常见原因。第二步在循环体开头加打印观察每次循环的关键变量值。比如打印count、height、savings一眼就能看出变量是在往前走还是在原地打转。写完一段循环如果第一次跑结果不对我从不猜直接print中间值这是最快的定位手段。第三步加保险丝。对于暂时没把握的循环设置一个最大迭代次数兜底count 0 max_attempts 10000 while condition_true: # 业务逻辑 count 1 if count max_attempts: print(达到最大迭代次数异常退出) break这个方法在写复杂逻辑时非常管用可以防止程序卡死导致“看起来像死机了”的风险。4.2 遍历时删除列表元素一个隐蔽的跳项坑这是一个经典大坑。比如你想把列表里的偶数全部删掉numbers [1, 2, 3, 4, 5, 6] for num in numbers: if num % 2 0: numbers.remove(num) print(numbers)你可能期望结果是[1, 3, 5]但实际运行后会发现结果是[1, 3, 5]吗不一定。这个写法很危险因为迭代过程中删除了元素列表的长度和索引都会变化可能会导致元素被跳过或者漏删。在某些数据分布下结果就是错的却看起来没错这才是最可怕的。更稳妥的做法是遍历原列表的一个副本在副本上迭代在原列表上删除numbers [1, 2, 3, 4, 5, 6] for num in numbers[:]: # 注意遍历的是副本 if num % 2 0: numbers.remove(num) print(numbers) # [1, 3, 5]用numbers[:]创建副本是最简单的解决方案。如果你要更高效还可以用列表推导式一步到位[x for x in numbers if x % 2 ! 0]。4.3 append之后全是同一个值循环里的引用陷阱还有一个坑更隐蔽那就是append到列表里的对象最后看起来全都一样。比如这段代码result [] temp {} for i in range(5): temp[value] i result.append(temp) print(result)结果会是什么呢是[{value: 4}, {value: 4}, {value: 4}, {value: 4}, {value: 4}]五个都变成了4。原因是temp是同一个字典对象每次循环只是修改它的内容append进去的是同一个引用最后所有指向都看到最后一次的值。顺序改一下就对了result [] for i in range(5): temp {} # 每次循环创建新的字典对象 temp[value] i result.append(temp) print(result) # [{value: 0}, {value: 1}, ...]这个坑就属于“不看文档永远猜不到”的类型。我的建议是循环里要往列表或外层容器塞对象时想清楚塞进去的是“新对象”还是“同一个对象的引用”。发现结果集体串值时优先检查是不是在循环外重复使用了同一个可变对象。4.4 常见问题速查表我整理了一份速查表方便你遇到问题时按图索骥现象可能原因排查与解决程序不结束或输出疯狂刷屏while条件永真循环体内没有更新条件变量检查条件变量更新加最大迭代次数保险丝循环结果比预期多一次或少一次range边界理解不当或嵌套循环内外层搞反手动模拟两三轮用print打印关键变量确认边界遍历列表时删除元素结果不对迭代过程中修改列表导致索引错位遍历副本numbers[:]或用推导式过滤append到列表的元素全变成最后的值循环外创建了可变对象并在循环内复用把对象创建移到循环体内确保每次都是新对象列表很长但代码特别慢嵌套循环层数过深或循环内重复执行重活估算执行次数把重操作移出循环考虑numpy/pandas替代循环明明满足条件却没执行判断条件写反或先更新状态后判断导致顺序错误逐行检查条件逻辑先手动按代码顺序走一遍另外插一句热词里那个err:23 数据错误(循环冗余检查)和Python循环毫无关系它通常是磁盘读取或文件复制时出现的校验错误属于系统底层的红土河山不是编程循环的问题。如果你在某个软件里看到这个提示不用往代码逻辑上想先去检查硬盘和文件完整性。5. 结尾一点个人的循环心得相信我循环写多了以后真正让你高效的往往不是某个花哨语法而是清晰的习惯。我在实际工作中最大的体会是写循环之前先想清楚“这个循环的入口条件、更新条件和出口条件各是什么”。这三件事想明白了不管是for还是while、不管嵌套多少层代码都不会跑偏。至于break、continue、for...else这些不是炫技它们本质上都是为了让“出口条件”表达得更直接。最后再分享两个小技巧。第一能用for就不要用while因为for的边界天生比while更可控在代码评审时也更容易被看懂第二循环里一定要留“后路”该break就break、该设上限就设上限千万别把程序放养成一匹脱缰野马。循环是编程里最基础的零件之一但也正因为基础把它吃透的人写出来的代码才会更稳、更快、更不容易翻车。
返回列表