Python 现代开发学习笔记

(五)分支、缩进与条件表达式

1. 把判断接到动作

现在把前面算出的真假值接到不同动作。if 后面写条件,再用冒号引出它控制的代码块;代码块中的语句向右缩进,同一层的语句对齐。下面每层用 4 个空格,符合本笔记采用的 PEP 8 缩进约定。

# 先处理输入的数值,再选择对应的输出动作。
minutes = 45
if minutes < 0:
    print("分钟数不能是负数")
elif minutes < 30:
    print("继续阅读")
else:
    print("已完成今天的目标")
# 这一行退回外层,所以无论前面选了哪个分支都会执行。
print("本次检查结束")

if/elif/else 从上往下选择第一个成立的分支;进入某个分支后,不会再去执行同一组中后面的分支。三行分支首部的 if、elif、else 对齐,各自控制的 print 再缩进一层。最后一行 print("本次检查结束") 回到行首,属于外层,选完分支后接着执行。

上例 45 < 0 为假,45 < 30 为假,最后执行 else。把分钟数改成 20,第二个条件成立,输出就变成 继续阅读。不需要先学习所有比较符,就能跟着数据走完整个判断。

2. 缩进决定语句的归属

把任务改成“达到目标时,输出提示并记录一次达标”。先看两个动作都缩进的写法:

# 两条缩进后的 print 都属于 if;20 分钟没有达到 30 分钟。
minutes = 20
if minutes >= 30:
    print("达到目标")
    print("保存达标记录")
# 回到行首后,继续执行外层语句。
print("本次检查结束")

20 >= 30 为假,整个 if 代码块被跳过,两条缩进后的 print 都没有执行。输出只有 本次检查结束。

现在只把第二条 print 向左移一层,其他文字和条件保持相同:

# if 现在只控制第一条 print。
minutes = 20
if minutes >= 30:
    print("达到目标")
# 这一行已退回外层,判断为假时也会继续执行。
print("保存达标记录")
print("本次检查结束")

这次输出为:

保存达标记录
本次检查结束

两段代码都能运行,记录动作发生的时机却不同。第二段在未达标时也输出了保存记录的文字;要让它跟随达标判断执行,就应像第一段那样,把它缩进到 if 下面。缩进改变了程序的执行逻辑。

同样的20分钟,记录动作属于if时被跳过,退回外层后则继续执行

图中先比较条件,再分别标出跳过的语句和下一条执行位置。读代码时可以从当前语句向上找包住它的较浅一层首部,判断它由哪个分支控制。Python 缩进语法

3. 嵌套代码块与逐层退出

达到 30 分钟算完成目标,达到 60 分钟再多记一颗星。第二次判断发生在已经达标的分支里面,就再向右缩进一层:

# 用同一次阅读记录观察外层和内层的判断。
minutes = 45
if minutes >= 30:
    print("达到目标")
    # 这条 if 属于外层分支;它控制的 print 再缩进到 8 个空格。
    if minutes >= 60:
        print("额外记录一颗星")
    # 回到 4 个空格,只离开内层 if,仍在外层达标分支内。
    print("保存达标记录")
# 回到 0 个空格,离开整个外层分支。
print("本次检查结束")

这里有三个层级:行首是外层;4 个空格的位置属于 minutes >= 30 的代码块;8 个空格的位置属于内层 minutes >= 60 的代码块。外层代码块包含内层的整条 if 语句,以及与它同层的其他动作。

沿 45 分钟走一遍:外层判断为真,先输出 达到目标;内层判断为假,只跳过 额外记录一颗星。随后读到缩进回到 4 个空格的 保存达标记录,它仍属于外层,所以继续输出。最后回到行首,输出 本次检查结束。

45分钟先进入外层分支,内层判断为假后回到4空格的位置,再退回行首继续
minutes 实际经过的判断 输出顺序
20 外层为假,内层也随整个代码块一起跳过 本次检查结束
45 外层为真,内层为假 达到目标 → 保存达标记录 → 本次检查结束
75 外层和内层都为真 达到目标 → 额外记录一颗星 → 保存达标记录 → 本次检查结束

缩进退回哪一层,就从那一层继续;结束一个代码块后,程序还会执行后面的语句。给嵌套判断增加 else 时,让它与准备配对的 if 对齐。下一节的循环、第三章的函数和第四章的类,也都通过缩进组织各自的代码块。

4. 空代码块与缩进报错

还没写好达标后的动作时,可以先用 pass 留一个位置:

# pass 是一条不做额外操作的语句,让这个分支先具有完整结构。
minutes = 45
if minutes >= 30:
    # 以后在这里补充达标处理,单独的注释不会成为可执行语句。
    pass
print("本次检查结束")

只写冒号和注释,解释器仍然没有读到代码块中的语句。pass 放在缩进后的语句位置,执行后继续往下走;它在这里不会提前结束程序,也不会重复执行这段判断。

报错或现象 对照代码检查的位置
IndentationError: expected an indented block 冒号后面缺少缩进的语句;把该分支的动作缩进进去,暂时留空时写 pass
IndentationError: unexpected indent 当前语句突然多缩进了一层,前面却没有引入这层代码块的首部
IndentationError: unindent does not match any outer indentation level 从内层退回时没有对齐已有外层;例如图中已有 0、4、8 个空格,却退到了 6 个空格的位置
TabError 检查同层语句是否混用了制表符和空格,将缩进统一为空格后再核对各层
程序能运行,动作却执行多了或少了 对照前面的两个版本,检查相关语句是否被移进或移出了代码块

空白行和单独的注释行不结束代码块,下一条语句的缩进才决定它属于哪一层。在编辑器里缩进或取消缩进后,把同一层的语句一起检查,再换一份输入验证输出。

5. 根据条件选择一个值

先选一个字符串再输出,可以写成完整分支:

# 两个分支都为 status 选择一个字符串。
minutes = 45
if minutes >= 30:
    status = "已完成"
else:
    status = "进行中"
print(status)

当目的只是二选一地得到一个值时,可以使用条件表达式:

# 先判断条件,取其中一个分支的值,再赋给 status。
minutes = 45
status = "已完成" if minutes >= 30 else "进行中"
print(status)

阅读顺序是“条件成立取左边,否则取右边”。它产生一个值;完整 if 语句还可以安排多条动作。多个条件相互嵌套时,展开分支更容易追踪。

笔记正文与全部题解 · Ctrl / ⌘ K