09 - 循环

发布时间:2026/5/26 16:37:49

09 - 循环 09 - 循环循环就是让一段代码重复执行。Python 有两种循环for和while。说真的我写 Python 以来可能 80% 的情况都在用 for。for 循环for 循环用来遍历一个可迭代对象列表、字符串、字典等等fruits[苹果,香蕉,橘子]forfruitinfruits:print(fruit)# 苹果# 香蕉# 橘子每次循环变量fruit会依次取列表里的每个值。遍历字符串forcharinPython:print(char)# P# y# t# h# o# n遍历字典person{name:小明,age:25}forkey,valueinperson.items():print(f{key}{value})range()当你需要循环固定次数时用range()# range(5) 生成 0, 1, 2, 3, 4foriinrange(5):print(i)# range(1, 6) 生成 1, 2, 3, 4, 5foriinrange(1,6):print(i)# range(0, 10, 2) 生成 0, 2, 4, 6, 8步长为 2foriinrange(0,10,2):print(i)# 倒序foriinrange(5,0,-1):print(i)# 5, 4, 3, 2, 1注意range(start, stop)也是左闭右开的range(1, 6)包含 1 但不包含 6。range()不会真的在内存里生成所有数字它是个惰性序列所以range(1000000)也不会占很多内存。enumerate()有时候你既要值又要索引可以这样写fruits[苹果,香蕉,橘子]# 不太好的写法foriinrange(len(fruits)):print(f{i}:{fruits[i]})# 更好的写法fori,fruitinenumerate(fruits):print(f{i}:{fruit})enumerate()同时给你索引和值代码更优雅。还可以指定起始索引fori,fruitinenumerate(fruits,start1):print(f第{i}个:{fruit})while 循环while 在条件为 True 的时候一直循环count0whilecount5:print(count)count1别忘了更新条件变量不然就死循环了# 死循环警告# while True:# print(停不下来了)死循环也不完全是坏事有些场景确实需要比如服务器一直监听请求但一定要有退出的方式。while 的适用场景while 更适合不确定要循环几次的情况# 用户输入直到正确为止passwordwhilepassword!python123:passwordinput(请输入密码)print(密码正确)# 不断减半直到小于 1n100whilen1:print(n)n/2break 和 continuebreak直接跳出整个循环foriinrange(10):ifi5:break# 到 5 就停print(i)# 输出0 1 2 3 4一个实际的场景——找东西names[小明,小红,小刚,小美]target小刚fornameinnames:ifnametarget:print(f找到了{target})breakelse:print(没找到)continue跳过本次进入下一次循环foriinrange(10):ifi%20:continue# 跳过偶数print(i)# 输出1 3 5 7 9for-else 语法你可能注意到上面有个for-else。这是 Python 的一个独特语法foriinrange(5):ifi10:print(找到了)breakelse:print(循环正常结束没有被 break)else块只在循环没有被 break 打断的情况下执行。如果 break 了else 就不执行。这个语法专门用在在列表里找东西的场景不过说实话很多人不知道这个语法包括一些写了好几年 Python 的人。你用不用都行知道有这回事就好。嵌套循环循环里面套循环# 九九乘法表foriinrange(1,10):forjinrange(1,i1):print(f{j}×{i}{i*j},end\t)print()# 换行输出1×11 1×22 2×24 1×33 2×36 3×39 ...嵌套循环的时间复杂度是乘积关系。两层各循环 n 次就是 n² 次。如果 n 很大比如几万代码会跑得很慢。遇到这种情况就该想想有没有更好的算法。zip()同时遍历多个列表names[小明,小红,小刚]scores[85,92,78]forname,scoreinzip(names,scores):print(f{name}{score}分)# 小明85分# 小红92分# 小刚78分zip()会把多个列表的对应元素打包成元组。如果长度不一样以最短的为准a[1,2,3]b[a,b]list(zip(a,b))# [(1, a), (2, b)]如果想以最长的为准用itertools.zip_longestfromitertoolsimportzip_longest a[1,2,3]b[a,b]list(zip_longest(a,b,fillvalue?))# [(1, a), (2, b), (3, ?)]循环中的一些实用技巧用 for 构建列表# 生成 1-10 的平方squares[]forxinrange(1,11):squares.append(x**2)# 用列表推导式更简洁第 17 章详细讲squares[x**2forxinrange(1,11)]遍历文件的每一行withopen(data.txt,r)asf:forlineinf:print(line.strip())这个不会一次把整个文件读进内存是一行一行读的大文件也不怕。用 _ 表示不需要的变量# 只关心循环次数不关心具体的值for_inrange(5):print(hello)_是一个约定表示这个变量我不需要忽略它。小心几个坑循环中修改列表# 错误示范边遍历边删元素numbers[1,2,3,4,5]forninnumbers:ifn%20:numbers.remove(n)print(numbers)# [1, 3, 5]不一定哦可能漏掉一些遍历的同时修改列表会导致索引错乱。正确的做法是遍历一份副本numbers[1,2,3,4,5]forninnumbers[:]:# numbers[:] 创建副本ifn%20:numbers.remove(n)或者用列表推导式更好numbers[nforninnumbersifn%2!0]浮点数做 while 条件# 可能出问题x0.0whilex!1.0:x0.1print(x)# 可能永远停不下来因为浮点精度问题用整数或者math.isclose()代替x0whilex10:x1print(x/10)for-else 和 while-else详细版前面简单提了一句这里展开说说。这个语法是 Python 独有的很多写了好几年 Python 的人都不知道。for-elsefor变量in序列:# 循环体if条件:breakelse:# 循环正常结束没有 break时执行最经典的场景——在列表里找东西# 找第一个大于 100 的数numbers[23,45,67,12,89]forninnumbers:ifn100:print(f找到了{n})breakelse:# 跑完了都没 break说明没找到print(没有找到大于 100 的数)不用 for-else 的话你得用一个标志变量foundFalseforninnumbers:ifn100:print(f找到了{n})foundTruebreakifnotfound:print(没有找到大于 100 的数)对比一下for-else 版本更简洁。while-else逻辑一样while 正常结束条件变 False时执行 else被 break 打断则不执行# 猜数字最多猜 3 次importrandom secretrandom.randint(1,10)attempts0whileattempts3:guessint(input(猜一个 1-10 的数字))attempts1ifguesssecret:print(猜对了)breakelse:# 三次都没猜对print(f次数用完了答案是{secret})什么时候不用 for-else说实话这个语法知道就好不强制使用。如果你的团队成员不熟悉这个语法用标志变量反而更容易理解。代码是写给人看的能看懂比简洁更重要。本章小结for遍历可迭代对象while在条件为 True 时循环range()生成数字序列enumerate()同时给索引和值break跳出循环continue跳过当前轮for-else/while-else中的 else 只在循环没被 break 时执行适合搜索场景zip()同时遍历多个列表别在遍历列表的时候修改它面试题Q1for-else语法是什么意思else 什么时候执行点击查看答案for-else中else块在循环正常完成没有被break时执行。如果循环被break提前终止else不执行。典型用法——搜索foriteminitems:ifitemtarget:print(找到了)breakelse:print(没找到)# 循环跑完了都没 breakwhile-else也是一样的逻辑。Q2range()的返回值是列表吗点击查看答案在 Python 3 中不是。range()返回的是一个range对象它是一个惰性序列不会在内存中生成所有数字。rrange(1000000)# 不会占很多内存print(type(r))# class rangeprint(len(r))# 1000000可以算长度print(r[5])# 5可以索引如果需要转成列表list(range(10))→[0, 1, 2, ..., 9]这是 Python 3 的改进Python 2 的range()会直接返回列表浪费内存需要用xrange()才是惰性的。Q3遍历列表时删除元素会有什么问题怎么解决点击查看答案遍历列表时直接删除元素会导致索引偏移可能跳过某些元素。# 有问题的代码nums[1,2,2,3]forninnums:ifn2:nums.remove(n)# 结果可能是 [1, 2, 3]漏删了一个 2解决方法遍历副本for n in nums[:]列表推导式推荐nums [n for n in nums if n ! 2]倒序遍历for n in reversed(nums)用 filternums list(filter(lambda n: n ! 2, nums))Q4enumerate()和zip()分别解决什么问题点击查看答案enumerate()解决既要索引又要值的问题。不用手动维护计数器变量。fori,valinenumerate(items):# i 是索引val 是值zip()解决同时遍历多个序列的问题。将多个序列的对应元素打包。forname,scoreinzip(names,scores):# 同时取两个列表两者都返回迭代器惰性的不会一次生成所有数据。如果长度不同zip以最短的为准。上一章 | 下一章函数 →

相关新闻