别只会 `range(10)`:Python 循环的“变速器”与“座位表”
你有没有过这种经历?写循环的时候,心里想的是“从 1 数到 10”,可手指一敲就是 range(1, 10),结果 10 死活不出现。或者你想倒着遍历一个列表,却写了个 range(len(lst), 0),然后盯着空白的输出发呆。
这不是你笨,而是你对 for 循环的“驾驶舱”还只熟悉一个挡位。今天我们就把这台机器的 步长(step)、字符串遍历 和 位置索引(enumerate) 这三个核心控件一次性摸透。相信我,搞懂它们,你写循环的姿势会从“骑自行车”直接升级成“开手动挡跑车”。
一、range 的第三只眼:步长
很多人把 range 当成一个只会从 0 开始数数的工具。但真正的老手知道,range(start, stop, step) 里的第三个参数 step 才是灵魂。
正步长:跨台阶
想象你爬楼梯,正常人一步一级。但如果你想两步并作一步,循环里的 step=2 就是这个“大跨步”。比如你写 for i in range(0, 11, 2),打印出来的就是 0 2 4 6 8 10。这就像小明的存钱罐——第一天存 0 元(基准日),之后每两天往里扔 2 元。代码跑一遍,他每个“存款日”的余额一目了然。
负步长:倒着下楼
倒着数数怎么写?range(10, -1, -1)。注意看,结束值是 -1,不是 0。为什么?因为 range 有个铁律:包含开头,不包含结尾。就像火箭发射倒计时,你喊到“10, 9, 8...1, 点火!”,但不会喊“0”再点火——0 那一刻已经是动作了。所以如果你想包含 0,必须把 stop 写成 -1。
这里有个经典的坑,几乎每个初学者都会踩:
# 错误示范:想打印 10 到 1,但什么都没输出
for i in range(10, 1):
print(i) # 啥也没有
为什么?因为默认步长是 +1,从 10 开始要“往上加”,可终点是 1(往下走),方向反了,循环直接罢工。步长的符号必须和 start → stop 的方向一致。想往下走,就得给个负步长。这个道理,就像下坡路你非要挂前进挡,车子当然不动。
二、字符串就是“字符串串”
字符串本质上是字符的有序序列。for c in "hello": print(c) 会像撸串一样,把每个字符挨个儿举到你面前。但注意,这串“珠子”是冻住的——你不能在循环里直接改它。
有次我让学生写个“密码检查器”,要求检测是否包含大写字母。有个孩子写出了这样的代码:
password = "P@ssw0rd"
has_upper = False
for char in password:
if char.isupper():
has_upper = True
break
这代码没问题。但另一个孩子试图在循环里把 char 改成大写再去比较,结果发现原字符串纹丝不动。这就是关键认知:char 只是一个拷贝的“临时工”,你改它,原字符串连眼皮都不抬一下。想修改字符串?你得新建一个,比如用列表收集再 join 回去。
三、enumerate:给每个元素发个座位号
遍历字符串或列表时,光有内容不够,很多时候你还想知道“这是第几个”。以前你可能这么干:
i = 0
for animal in ["小狗", "小猫", "小兔"]:
print(f"第{i}个动物是{animal}")
i += 1
手动维护计数器,就像排队时自己默默数数,容易数岔。而 enumerate 就是那个自动发号机:
for index, animal in enumerate(["小狗", "小猫", "小兔"], start=1):
print(f"第{index}个动物是{animal}")
注意 start=1 这个参数,它让编号从 1 开始,更符合人类的直觉。很多新手会忘记解包——直接 for item in enumerate(lst) 拿到的是个元组 (0, '小狗'),还得自己再拆一次。既然 Python 都给你准备好了双变量解包,别客气,直接用。
四、循环 + if:筛子与渔网
把循环和条件判断结合,你就拥有了筛选的能力。比如找出 1 到 100 里能被 3 整除的数:
for num in range(1, 101):
if num % 3 == 0:
print(num)
这就像用一张网眼大小为 3 的渔网捞鱼——只有“余数为 0”的鱼能漏过去被你捕获。这个模式太常用了,以至于后来 Python 专门发明了列表推导式 [x for x in range(1,101) if x%3==0] 来简化它。
五、实战解剖:一道题看透步长陷阱
来看一道非常经典的考察题:
执行
for i in range(10, 0, -2): print(i, end=' '),输出是什么?
很多人第一反应是“10 8 6 4 2 0”。错! 这就是没吃透“不包含 stop”的铁律。stop=0,意味着当 i 减到 0 时,循环立刻停止,0 根本不会被打印。正确答案是 10 8 6 4 2。
这道题的精髓在于两点:
- 步长为负时,循环条件是
i > stop(不包含等于)。 - 步长绝对值是 2,所以跳过了所有奇数。
如果你把题目改成 range(10, -1, -2),输出才会变成 10 8 6 4 2 0 -2——因为 stop=-1,-1 不包含,但 0 和 -2 都在范围内。改一个数字,结果天差地别,这就是边界条件的威力。
六、实战解剖:字符串里的“幽灵空格”
再看一道判断题:for c in 'hello': print(c) 会输出包括空格在内的字符。这说法对吗?
错得离谱。 'hello' 这五个字母里压根没有空格,你让循环输出什么?输出空气吗?这道题表面考遍历,实际考的是你是否真的理解“遍历的对象是谁”。很多人的思维定势是“字符串嘛,总有点空白字符”,但 Python 里字符串就是精确的字符序列——里面没空格就是没空格,不会因为你 print 就自动加空格(除非你用 print(a, b) 这种多参数形式,默认分隔符才是空格)。
如果真想输出空格,你得写 'hel lo'——在字符串里显式放一个空格字符,循环才会把它当“串”上的一颗珠子撸出来。
七、终极组合拳:给句子里的单词编号
现在我们把所有技能串起来。假设用户输入一句话,你想给每个单词编号:
sentence = "I love Python"
words = sentence.split() # 用空格切分,得到 ['I', 'love', 'Python']
for num, word in enumerate(words, start=1):
print(f"第{num}个单词是:{word}")
这里 split() 是切菜刀,enumerate 是编号员,f-string 是排版工。三者配合,行云流水。注意 split() 默认按任意空白字符切分,包括多个连续空格——这比手动 split(' ') 更健壮。
八、写在最后:循环的三个境界
学 for 循环,有三个境界:
- 会数数:用
range做简单重复。 - 会挑着数:掌握步长,能正着数、倒着数、跳着数。
- 会边数边看:用
enumerate同时拿到内容和位置,配合if做筛选。
你现在处于哪个境界?如果还在第一层,别急,把今天的步长和 enumerate 练熟,你就能直接跳到第二层和第三层。下一步可以挑战嵌套循环(打印九九乘法表)或者列表推导式(一行代码替代三层循环),你会发现,循环的世界远比想象中宽广。
记住:循环不是笨重地重复,而是有节奏地控制。当你掌握了步长这个“变速器”和 enumerate 这个“座位表”,你写的代码会像一首韵律整齐的诗。
关于作者
我是赵老师,持有 NOI 信息学奥赛教练证书,拥有 15 年以上的软件开发经验,从事信息学少儿编程教学已有 8 年时间。
这些年累计帮助 多名 学生通过编程特长升入自己心仪的目标学校。
如果你在编程学习上有任何疑问,欢迎联系我:18620372957(微信同号)