# Python 的 for 循环：让同一段代码自动重复跑很多遍

承接上一篇的 if 判断：用 range() 控制重复次数，用缩进圈出循环体，看清每一轮取数字、执行、回头的完整过程，并绕开缩进与“差一”两个坑。

> 循环与重复执行 · range 函数 · 自动化入门 · 约 7 分钟 · 10 月 08 日

## 本篇要点

1. `for` 循环的写法是 `for 变量 in range(...)` 加冒号、换行缩进，缩进块是循环体，会被重复执行。
2. `range(5)` 交出的是一串数字 0、1、2、3、4，循环体因此跑 5 遍；`range(a, b)` 从 a 开始、不到 b，所以想从 1 数到 5 要写 `range(1, 6)`。
3. 每一轮固定三步：取出下一个数字装进循环变量、完整执行一遍循环体、回头取下一个；数字取完就跳出循环，继续执行循环后面的代码。
4. 循环变量是普通变量，每轮开头被重新赋值，所以循环体里改它不影响循环次数；循环结束后它保留最后一个值，而如果 range 给不出数字，循环体一次都不执行、它也不会被赋值。
5. 缩进决定哪几行被重复：循环体外的收尾语句只执行一次，多缩进 4 格就会跟着循环重复。
6. 累加这类活儿要把“账本”变量在循环外先初始化，再在循环体里逐轮更新；把初始化挪进循环体，每轮清零，1 到 4 的求和就只剩 4 而不是 10。
7. 循环体里可以放 `if` 判断，两层缩进分工明确：循环决定重复几遍，`if` 决定这一遍说什么。

---

上一篇结尾你写好了一个把关脚本：数据是空的、太少、正常，各说各的话。可它一次只能判断一份数据。真实的报表有几百行，每一行都要做类似的检查或处理——你不可能把那段代码复制八百遍，复制出来的东西只要改一次规则，就得回头改八百处。

这一篇给程序装上第一台“自动重复机”：`for` 循环。今天只把一件事讲透——**怎么让一段代码按你指定的次数一遍一遍地跑，以及每一遍到底发生了什么。**

## 先把结构写出来

最短的一个循环长这样：

```python
for 次数 in range(5):
    print("滴——正在读取数据……")
```

运行它，你会发现这句话被打印了 **5 遍**，而不是一遍。

先看结构。`for` 这一行以英文冒号结尾，中间分成三段：`for` 关键字、一个自己起的名字（叫它**循环变量**）、以及 `in range(5)`。下一行缩进 4 个空格，是**循环体**——就是那部分要被反复执行的代码。

这个结构和上一篇的 `if` 是同一个套路：**冒号开门，缩进圈地**。区别只在含义——`if` 后面缩进的东西“条件成立时执行一次”，`for` 后面缩进的东西“一遍又一遍地执行”。

## `range()` 交出来的到底是什么

`range(5)` 不是让程序“循环 5 次”的开关，它在做一件更具体的事：**生成一串数字交出去**。`range(5)` 给出的五个数字是

0、1、2、3、4

五个数字，所以循环体跑五遍。注意它从 0 开始，而且拿不到 5。[1]

要让数字从 1 开始、数到 5 为止，写成 `range(1, 6)`——它给出 1、2、3、4、5。第二个参数是**终点，但终点不算在内**，所以想数到 5 就必须写 6。这个“差一”是初学最常见的错误来源，记住一句话就够了：`range(a, b)` 给出的是从 $a$ 开始、**不到 $b$** 的整数。

## 一遍一遍，到底发生了什么

看这个会真正用上数字的例子：

```python
for 序号 in range(1, 4):
    print(f"第 {序号} 次巡检，设备状态正常。")
print("巡检结束。")
```

输出四行：三次“第 N 次巡检”，最后一行“巡检结束。”。

`range(1, 4)` 交出的数字是 1、2、3。`for` 拿到这串数字后，每一轮固定做三件事：

1. 取出下一个数字，装进循环变量 `序号`；
2. 把缩进块里的代码完整执行一遍；
3. 回头取下一个数字，直到数字取完——循环结束，程序接着执行循环后面那行。

| 轮次 | 循环变量 `序号` | 这一轮打印的内容 |
| --- | --- | --- |
| 第 1 轮 | 1 | 第 1 次巡检，设备状态正常。 |
| 第 2 轮 | 2 | 第 2 次巡检，设备状态正常。 |
| 第 3 轮 | 3 | 第 3 次巡检，设备状态正常。 |
| 数字取完 | —— | 巡检结束。 |

画成流程图是这样：

```mermaid
flowchart LR
  A["for 序号 in range(1, 4)："] --> B{"数字取完了吗？"}
  B -- "还有" --> C["取出下一个数字，装进 序号"]
  C --> D["执行缩进块里的所有代码"]
  D --> B
  B -- "取完了" --> E["循环结束，执行循环后面的代码"]
```

这张图里有两个关键点。第一，**循环体的入口在每一轮都相同**：第 2 轮不是接着第 1 轮往下走，而是回到循环体开头重新走一遍，唯一变的是循环变量里的值。第二，**循环变量是普通变量**，只不过每轮开头会被重新赋值一次 [2]。所以循环结束后，`序号` 并不会消失，它保留着最后那个数字 3；反过来，如果 `range()` 一个数字都给不出来（比如 `range(0)`），循环体一次都不执行，循环变量也就根本没被赋过值。

## 循环体里可以放什么

循环体里能放多少行都没关系，只要是同样的缩进，它们就一起被重复。而且循环体里可以放上一篇学的整套 `if` 判断——这正是“对一批数据逐条把关”需要的组合。

```python
for 件数 in range(1, 6):
    if 件数 < 3:
        print(f"{件数} 件：数量太少，需要核对。")
    else:
        print(f"{件数} 件：正常。")
```

这里出现了两层缩进，要看清谁管谁：`if` 和它的 `else` 缩进 4 个空格，属于循环体，每一轮都要走一遍；两个 `print` 缩进 8 个空格，分别属于 `if` 和 `else`，每轮里只会执行其中之一。运行结果是：1 件、2 件说“需要核对”，3 件、4 件、5 件说“正常”——五轮，五句输出。

说白了，**循环负责“重复几遍”，`if` 负责“这一遍说什么”**，两者各管一段，靠缩进分层。

这里得老实交代一点：上面的“件数”是从 `range()` 里临时造出来的数字，只为演示机制。真实工作里这些数字来自表格的一列，等学了能装一串数据的容器，把 `range(1, 6)` 换成那份数据就能直接用。这是下一篇的事。

## 常备一个“账本”：循环里做累加

再学一个几乎处处要用的招式。假如要算 $1+2+3+4$：

```python
合计 = 0
for 份数 in range(1, 5):
    合计 = 合计 + 份数
print(f"1 到 4 的和是 {合计}")
```

输出“1 到 4 的和是 10”。关键在于 `合计 = 0` 这一行：它在**循环之前**、且**没有缩进**，所以只执行一次，相当于先准备好一个空账本。之后每一轮，循环体里那行 `合计 = 合计 + 份数` 都在做同一件事：把这一轮的数字加到账本上。

| 轮次 | `份数` | 执行后 `合计` 的值 |
| --- | --- | --- |
| 开始前 | —— | 0 |
| 第 1 轮 | 1 | 1 |
| 第 2 轮 | 2 | 3 |
| 第 3 轮 | 3 | 6 |
| 第 4 轮 | 4 | 10 |

对照这张表能看清一件事：`合计` 从头到尾只有一个，每一轮都在改它，所以值会累积下来。如果把 `合计 = 0` 挪进循环体（多缩进 4 个空格），它每轮开头都被清零，前面的累加全部作废，最后只剩最后一轮的结果 4，而不是 10。**准备变量的那行在循环外、干活的那行在循环内**，这是两种缩进带来两种结果的最典型一处。

## 三个最容易踩的坑

**一、缩进决定了哪几行被重复。** 这两段代码输出的行数完全不同：

```python
for 序号 in range(3):
    print(f"处理第 {序号} 份")
print("全部处理完毕")
```

第一段打印“处理第 1/2/3 份”，然后打印一次“全部处理完毕”。如果把最后那行也缩进 4 格，它就成了循环体的一部分，会被打印三次——一句话被说三遍，程序不报错，但你想要的“收尾提示”变成了“重复播报”。

**二、重复次数不等于终点那个数字。** `range(1, 5)` 跑 4 遍（1、2、3、4），不是 5 遍。写成 `range(1, 6)` 才是 5 遍。写完数一下括号里到底给出几个数，比背口诀可靠。

**三、在循环体里给循环变量重新赋值没有用。** 比如在循环里写一行 `序号 = 100`，下一轮开头 `序号` 还是会被下一个数字覆盖掉，循环该跑几遍还是几遍 [2]。原因是每一轮的循环变量都由 `for` 那一行重新指定，你在循环体里改的是它“这一轮的临时值”，改不了循环的节奏。

## 今天就到这里

现在你手里有三个能力：变量记住数据、`if` 看情况走不同的路、`for` 把同一段代码重复指定的次数。把把关脚本套进循环，已经能按次数逐条检查并累计结果了——只是这些数据目前还得靠 `range()` 造出来。

但这台重复机还带着一个明显的限制：**重复的次数要么是你写死的（`range(5)`），要么是你数好写进去的（`range(1, 6)`）。** 现实里的重复通常不这样——表格有多少行就该跑多少遍，今天是 200 行，明天是 1500 行，你不会想每天先数一遍再改代码。

要突破这一步，得让 `for` 直接跟一份数据打交道，而不是先跟一串数字打交道。那需要的是一种能装下一整列数据的容器，也是下一篇的主题。

## 术语表

- for 循环：让同一段代码按次数反复执行的语句，写法是 `for 变量 in 一串数字:` 加缩进块。
- 循环变量：`for` 后面那个名字，每一轮开始被赋上下一个新值，循环体里就用它来干活。
- 循环体：`for` 那行下面缩进的代码，会被完整重复执行，直到数字取完。
- range()：用来生成一串整数交给 `for`；`range(5)` 给出 0 到 4，`range(a, b)` 从 a 到 b 但**不含 b**。
- 累加器（账本变量）：在循环外先赋值初始化、在循环体内逐轮更新的变量，用来把每一轮的结果攒起来。

## 来源

1. [Python 官方教程（中文）：4.3 range() 函数——生成的序列不含终止值、可不从 0 开始、可给步长](https://docs.python.org/zh-cn/3/tutorial/controlflow.html)
2. [Python 语言参考（中文）：8.3 for 语句——依次把每个条目赋给目标变量并执行代码块，循环体中对目标变量的赋值会被下一个条目覆盖](https://docs.python.org/zh-cn/3/reference/compound_stmts.html)

---

原文：https://pangzhengboyin.com/articles/python-for-loop-and-range-3aa5b410

> **庞征博引** · 想学的，慢慢都会
>
> 庞征博引是把想学的东西写成连载的 AI 学习工具。说出想学什么，它会先了解你的基础，再把主题写成一篇篇 5–10 分钟能读完的文章；边读边问，接下来学什么跟着你走。这篇就是这样写出来的。
>
> 开始你自己的连载 → https://pangzhengboyin.com
