🍓 第六章 开发 Python 程序
📄 一句话概括

前两节写的程序都是"从上往下一条道走到黑"。本节给程序装上"判断力"和"耐力"——让它能根据条件选路(if)、能重复干活(while / for)。 本节还有一件 Python 特有的大事:第一次正式使用缩进。在 Python 里,缩进不是"排版好看",它是语法的骨架——程序的从属关系全靠它表达。

📝 本节和前面几节的关系

6-1 说过 Python 用缩进代替大括号;6-3 用过判断(temp > 20 and temp < 30,结果是 True / False)。 本节把这两样拼起来:if 后面接的就是 6-3 那种"能算出真假"的表达式;而"哪几行属于这个 if",靠缩进表示。 读法建议:本节所有例子都建议在交互模式里敲,但有一个例外——多行结构(if、for、while)在交互模式里要连着敲(Python 会用 ... 提示你"接着说")。觉得别扭就直接写 .py 文件,那种手感更像正经开发。

一、程序的三种基本结构

所有程序(不夸张,是所有)都由三种结构拼出来:

结构 干什么 打比方 Python 里靠什么
顺序 一条接一条往下走 按清单买菜 默认就是(你什么都不用做)
分支 满足条件才做,否则走另一条 下雨就打伞 if / elif / else
循环 重复做,直到该停 绕操场跑十圈 while / for

顺序结构你已经会了(前两节的代码全是)。本节讲后两种。

二、缩进:Python 的"隐形大括号"

这是 Python 最独特、也最容易踩的地方。必须先讲清它,后面才讲得下去。

在 C++ 里,一段代码用 { } 圈起来:

if (5 > 3) {
    printf("这是 if 里面的\n");
}
printf("这句一定执行\n");

在 Python 里,没有大括号,靠缩进表示"这几行属于这个 if":

if 5 > 3:
    print("这是 if 里面的")
print("这句一定执行")

两条硬规矩:

  1. 冒号 : 不能忘——if 条件: 后面那个冒号,是"下面是属于我的代码"的信号。
  2. 属于它的行必须缩进——Python 社区统一用 4 个空格。
⚠️ 三种最典型的缩进错误

① 该缩进没缩进:

if 5 > 3:
print("坏了")     # ❌ IndentationError: expected an indented block

② 缩进量不一致(同一层里有的 4 格有的 2 格):

if 5 > 3:
    print("A")
  print("B")       # ❌ IndentationError: unindent does not match

③ Tab 和空格混用:

# 肉眼看着对齐,其实一个是 Tab 一个是 4 个空格
# ❌ TabError: inconsistent use of tabs and spaces in indentation

第 ③ 种最阴——肉眼看不出差别,只能靠报错信息。解法:用编辑器(Thonny 会自动统一),别手打。

2.1 缩进决定了"谁属于谁"——看一个真实的坑

a = 50

if a > 0:
    print("是正数")
    if a > 100:
        print("而且很大")
else:
    print("不是正数")

这个 else 对应的是哪个 if?答案:外层那个 if a > 0:——因为 else 和它缩进一样齐。

再看一个改坏的版本:

a = 50

if a > 0:
    print("是正数")
    if a > 100:
        print("而且很大")
    else:
        print("但不到 100")      # 这个 else 对的是内层 if

只把 else 往里挪了 4 格,意思就完全变了。 这就是"缩进即语法"的分量——在 C++ 里,你怎么排版都不影响程序;在 Python 里,挪一格就是改逻辑。

💡 一个能救命的习惯

写 if / for / while 的时候,先敲冒号、回车,让编辑器自动缩进好那 4 格,再写内容。 手动"先写内容再补缩进"是缩进错误的头号来源。

三、分支:if / elif / else

3.1 三种形态

形态一:只有 if(满足就做,不满足就跳过)

temp = 35
if temp > 30:
    print("太热了")

形态二:if + else(两条路选一条)

temp = 35
if temp > 30:
    print("太热了")
else:
    print("还好")

形态三:if + elif + else(多条路选一条)

score = 78

if score >= 90:
    print("优秀")
elif score >= 60:
    print("及格")
else:
    print("不及格")

输出:及格

⚠️ 是 `elif`,不是 `else if`

C++ / Java 里写 else if,Python 里必须合成一个词 elif(else if 的缩写)。 写成 else if 会直接语法错误。这是从 C 系语言过来最常打错的一处。

3.2 判断顺序很重要:从上往下,命中就走

elif 是"前面都没中,才轮到我"。所以条件和顺序密切相关:

score = 95

# 顺序对了
if score >= 90:
    print("优秀")            # → 输出这个,后面的 elif 不再看
elif score >= 60:
    print("及格")

# 顺序写反了(把 >= 60 放前面)
if score >= 60:
    print("及格")            # → 95 也进了这条,输出"及格",错的!
elif score >= 90:
    print("优秀")            # 永远不会执行到

记住一句话:范围大的条件往下放、范围小的往上放。

3.3 if 后面接的"不一定是比较"

Python 会把 if 后面的东西自动转成真假(就是 6-3 学的 bool()):

name = input("你叫什么?")

if name:                    # 等价于 if name != ""
    print(f"你好,{name}")
else:
    print("你什么都没输入")

这种写法非常常见——判断"有没有内容""列表空不空""有没有拿到值",一律直接写 if 变量:。

💡 能当"真"和"假"的东西(回扣 6-3)

假:False、0、0.0、""(空字符串)、[](空列表)、None 真:其他一切,包括 "0"(非空字符串)和 "False"(一段文字) 所以显示"是否成功"的结果时千万不能直接把字符串拿去做判断——它的内容是 "False" 也是"真"。

四、循环:while 与 for

4.1 while:不知道做几次,只知道"什么时候停"

count = 0
while count < 5:
    print(f"第 {count + 1} 次")
    count += 1
print("循环结束")

输出:

第 1 次
第 2 次
第 3 次
第 4 次
第 5 次
循环结束

while 的三个零件(缺一个就出事):

零件 作用 少它会怎样
初始值(count = 0) 从哪儿开始 报 NameError
条件(count < 5) 什么时候继续 语法错误
改变量(count += 1) 让条件最终会假 死循环!
⚠️ 忘了第三个零件 = 死循环
count = 0
while count < 5:
    print("永远停不下来")     # count 一直是 0,条件永远为真

程序会一直打印下去,直到你按 Ctrl + C 强行打断。 Ctrl + C 是你在树莓派上最该记住的救命键(3-5 讲过:它发的是"中断信号")。

注意:死循环会让 CPU 满转、树莓派发热,不是"无害的卡住"——发现了赶紧 Ctrl + C。

4.2 for:把"一串东西"挨个过一遍

这里有个和 C++ / Java 完全不同的地方,先说清楚:

📝 Python 的 `for` 不是"计数循环",是"遍历循环"

C++ 的 for (int i = 0; i < 5; i++) 是让你自己管计数器:设初值、判断、加一,三件事都你写。 Python 的 for 是"把这个串里的每个东西,挨个拿出来用一次"——叫遍历(iterate)。 它不管数字,它管的是"一串东西"(一串数字、一串字符、一串数据都行)。

for i in range(5):
    print(i)

输出:

0
1
2
3
4

注意两件事: 1. 从 0 开始(不是 1)——这是所有编程语言的惯例,因为"偏移量"从 0 数最方便。 2. range(5) 给出 0、1、2、3、4 共五个数——不含 5。这叫"左闭右开"。

range() 的三种写法:

写法 给出 个数
range(5) 0 1 2 3 4 5 个
range(2, 5) 2 3 4 3 个(含头不含尾)
range(1, 10, 2) 1 3 5 7 9 5 个(第三个参数是步长)
💡 "含头不含尾"为什么这么设计

因为这样 range(a, b) 的个数正好是 b - a(range(2, 5) 就是 3 个),不用 ±1 地算,很省脑子。 但代价是——想数到 10 必须写 range(1, 11)。这是新手最常数的错一格的地方。

for 不只能遍历数字——字符串也行:

for ch in "Pi":
    print(ch)

输出:P 然后 i(每个字符一个)。这个能力后面用到极多(6-6 会展开)。

4.3 break 和 continue:提前跳出去 / 跳过这一轮

for i in range(1, 11):
    if i == 4:
        continue          # 跳过 4,继续下一轮
    if i == 8:
        break             # 到 8 就直接结束整个循环
    print(i)

输出:1 2 3 5 6 7

关键字 作用 打比方
break 结束整个循环 找到想要的就收工
continue 跳过这一轮剩下的,直接进下一轮 这个不要,看下一个

4.4 while True + break:最常用的写法

想让用户"反复输入,直到他输对为止",但你事先不知道要几轮——标准写法是:

while True:
    answer = input("请输入 yes 或 no:")
    if answer == "yes":
        print("好的,继续。")
        break                 # ← 唯一的出口
    elif answer == "no":
        print("那就算了。")
        break
    else:
        print("只认 yes 或 no,再来一次。")

这不是"死循环写错了",而是故意写的"无限循环 + 出口"。 那些"输错就重来"的程序全是这个套路——本节的 Demo 就是它。

五、动手 Demo:猜数字游戏

把本节的判断和循环全用上。在命令行里写:

cd ~/projects
nano guess.py

内容:

import random                          # 下一节会正式讲"模块",这里先用

secret = random.randint(1, 100)        # 随机想一个 1~100 的整数
tries = 0                              # 记次数

print("我想了一个 1 到 100 之间的整数,你来猜。")

while True:                            # 一直循环,直到猜对才 break 出去
    guess = int(input("请输入你的猜测:"))
    tries += 1

    if guess < secret:
        print("小了,再大一点。")
    elif guess > secret:
        print("大了,再小一点。")
    else:
        print(f"猜对了!答案就是 {secret},你一共猜了 {tries} 次。")
        break

跑:

python3 guess.py

看到什么算成功(每次运行的数字都不一样,因为答案是随机的。下面是一次真实过程的示例):

我想了一个 1 到 100 之间的整数,你来猜。
请输入你的猜测:50
小了,再大一点。
请输入你的猜测:75
大了,再小一点。
请输入你的猜测:63
小了,再大一点。
请输入你的猜测:69
猜对了!答案就是 69,你一共猜了 4 次。

读结果:

你输入 程序判断 为什么
50 小了 50 < 69 → 走第一个 if
75 大了 75 > 69 → 第一个 if 不中、elif 中
63 小了 又进第一个 if
69 对了 前面都不中 → 进 else,打印后 break 退出循环

这个程序里有本节全部的知识点:

代码 用到的知识
while True: 无限循环
if / elif / else 三分支判断(顺序很关键)
break 唯一出口
tries += 1 增强赋值(6-3)
f"...{secret}..." f-string(6-3)
int(input(...)) 输入是字符串,要转(6-3)
💡 试着改两个地方,感受更深

① 把 random.randint(1, 100) 改成 random.randint(1, 10) —— 猜的次数会明显变少。 ② 故意输入一个字母(比如 abc)——程序会崩,报 ValueError。这不是你写错了,而是"用户输入不可信"这个真问题——后面讲异常处理时会正式解决它(先在 6-2 学的报错读法里确认一下:是不是 line N 指出了 int(...) 那一行)。

六、30 秒实验

实验一:亲手制造一次 IndentationError

nano indent_test.py

写入(print 故意不缩进):

if 5 > 3:
print("坏了")

跑 python3 indent_test.py,会看到:

  File "/home/你的用户名/projects/indent_test.py", line 2
    print("坏了")
    ^
IndentationError: expected an indented block after 'if' statement on line 1

认识这个报错名——IndentationError,Python 独有的错误类型。以后看到它,第一反应就是"缩进"。

实验二:乘法表(两层循环,练嵌套)

for i in range(1, 5):
    for j in range(1, i + 1):
        print(f"{j}x{i}={i * j}", end="  ")     # end="  " 表示"别换行,用两个空格隔开"
    print()                                      # 空的 print() 就是"换一行"

输出:

1x1=1  
1x2=2  2x2=4  
1x3=3  2x3=6  3x3=9  
1x4=4  2x4=8  3x4=12  4x4=16

读法(验证第一行和最后一行): - 外循环 i=1:内循环 range(1, 2) 只给 j=1 → 输出 1x1=1 ✅ - 外循环 i=4:内循环 range(1, 5) 给 j=1,2,3,4 → 依次输出 1x4=4、2x4=8、3x4=12、4x4=16 ✅ - 每次外循环结束,那个空的 print() 负责换行 —— 这就是它为什么是一行一行长的三角形。

end=" " 是什么:print() 默认结束时会自动换行;加 end="..." 就改成"用指定的东西结尾",这里用两个空格,所以同一行的几个算式能挤在一起。

实验三:range 的三种写法,亲手对一遍

>>> list(range(5))
[0, 1, 2, 3, 4]
>>> list(range(2, 5))
[2, 3, 4]
>>> list(range(1, 10, 2))
[1, 3, 5, 7, 9]
>>> len(list(range(2, 5)))
3

(list(...) 是"把一串东西显示出来"的办法,len(...) 是数个数——这两个下一节 6-5 / 6-6 会正式讲。)

七、常见误区

  1. "缩进随便,别歪就行" —— 缩进是语法,而且挪一格就改逻辑(2.1 那个 else 的例子)。同一层统一 4 个空格,别混 Tab。
  2. "else if 分开写" —— Python 里必须合起来写 elif。写 else if 直接语法错误。
  3. "for i in range(5) 会数到 5" —— 数到 4。range 是"含头不含尾"。想数到 5 要写 range(1, 6)。
  4. "循环里改 i 能让它多跑几轮" —— 不行。Python 的 for 每一轮都会重新从序列里取下一个值,你在循环体里改 i 是白改: python for i in range(3): i = 100 # 没用,下一轮 i 又变成 1 print(i) # 输出 100 100 100 (这一点和 C 的 for 很不同,想控制进度请用 while。)
  5. "死循环无所谓,反正卡住而已" —— CPU 会满转、树莓派会发烫(本课后面要讲散热)。发现卡住了:Ctrl + C。
  6. "while 条件: 里的条件只在开始时判断一次" —— 每一轮都判断。所以循环体里必须有一个能改变它的动作,否则永远出不去。
  7. "字符串 "False" 是假的" —— 它非空,所以是真。if "False": 会执行!只有 False(布尔值本身)、0、""、None 这些才是假的。
自测三问(点开对答案) 1. Python 里"哪几行属于这个 `if`"靠什么表示?和 C++ 有什么不同? 2. `range(1, 6)` 会给出哪些数字?一共几个? 3. `break` 和 `continue` 有什么不同? **参考答案** 1. **靠缩进**(同一层统一 4 个空格)。**C++ 用大括号 `{ }` 圈起来,缩进怎么乱放都不影响程序**;Python 里**缩进就是语法本身**——而且它决定的正是"从属关系",所以把 `else` 挪一格,`else` 或许就配给另一个 `if` 了,**逻辑会静默地变掉**。 2. **`1 2 3 4 5`,一共 5 个**。`range(a, b)` 是**含头不含尾**:从 1 开始、到 6 之前停,所以真给出的是 1 到 5。好处是**个数正好等于 `b - a`(6−1=5)**,不用 ±1 地算。 3. **`break` 结束整个循环**("找到就走人");**`continue` 只跳过这一轮剩下的部分,接着跑下一轮**("这个不要,看下一个")。

相关笔记