控制流

你之前写的每个程序的运行方式都是一样的:从上到下,一次一行。这对简单的脚本有效,但真实的程序需要做出决策和重复工作。测验需要检查答案是否正确。游戏需要一直运行到玩家赢或输。本章介绍如何让程序分支和重复。
比较
在做出决策之前,你需要比较事物。比较运算符返回 True 或 False。最重要的是早期正确掌握:= 赋值,== 检查两个值是否相等。混淆它们是初学者最常见的错误之一。
5 > 3 # True
5 < 3 # False
5 == 5 # True (注意:双等号;= 是赋值,== 是比较)
5 != 3 # True ("不等于")
5 >= 5 # True ("大于或等于")
5 <= 4 # False ("小于或等于")= 和 == 的区别困扰了几乎每个人。赋值(=)存储一个值;比较(==)检查两个值是否相同。
你也可以比较字符串。Python 按字母顺序逐字符比较它们,这在字符串章节中有更详细的介绍:
"apple" == "apple" # True
"apple" < "banana" # True (a 在 b 之前)
"apple" == "Apple" # False (区分大小写)= 存储一个值,== 检查两个值是否相等。当我累的时候我仍然会放慢速度并检查,所以如果它困扰你,你不是一个人。比较返回 True 或 False,它们也适用于字符串,按字母顺序。 组合条件
and、or 和 not 组合比较。and 要求两边都为真。or 要求至少一边为真。not 翻转结果。这让你能表达真实的条件,比如"分数及格且用户活跃"。
age = 25
score = 88
age >= 18 and score >= 80 # True (两边必须都为真)
age < 18 or score >= 80 # True (至少一边必须为真)
not age >= 18 # False (翻转结果)and 要求两边。or 要求至少一边。not 反转。
and 要求两边都为真,or 要求至少一边,not 翻转答案。这涵盖了你将写的大多数真实条件,比如"分数及格且用户活跃"。Python 一旦知道结果就停止检查。 真值和假值
Python 中的每个值都有一个布尔解释,即使它不是 True 或 False。空字符串、零、空列表和 None 在条件中都表现得像 False。其他所有东西都表现得像 True。这意味着 if results: 检查列表是否非空,无需写 if len(results) > 0:。
# 这些都在条件中表现得像 False:
False, 0, 0.0, "", [], {}, (), None
# 其他所有东西都表现得像 True这意味着 if results: 是说"如果列表不为空"的自然方式,if name: 检查字符串是否有任何内容。
False:0、""、[] 和 None。其他所有东西算作 True。这就是为什么 if results: 读起来是那么干净的"如果列表中有什么东西",不需要 len()。 if / elif / else
if 语句仅当其条件为 True 时运行一个代码块。elif 在第一个为假时添加更多条件检查。else 捕捉任何不匹配的东西。Python 使用缩进而不是花括号来定义每个块内的内容。
score = 87
if score >= 90:
print("A 等级")
elif score >= 80:
print("B 等级")
elif score >= 70:
print("C 等级")
else:
print("C 以下")规则:
if是必需的,总是首先出现elif("else if"的缩写)是可选的,你可以有任意多个else是可选的,处理任何不匹配的东西,最后出现- Python 使用缩进(4 个空格)来标记每个块内的内容;没有花括号
缩进不是可选的或仅仅是样式。Python 使用它来定义结构。不一致的缩进是语法错误。
if 在条件为真时运行其块,elif 检查更多情况,else 捕捉其余的。只有第一个匹配运行。早期困扰我的事情:缩进是告诉 Python 每个块开始和结束位置,所以保持一致。 单行条件
对于简单的是/否赋值,Python 有一个称为三元表达式的紧凑单行形式:value_if_true if condition else value_if_false。仅当逻辑很小并且读起来像一个句子时才使用它。
label = "pass" if score >= 50 else "fail"这是一个三元表达式;它读起来像一个句子。当逻辑很小时使用它。对于任何涉及 elif 的东西,写完整版本。
value_if_true if condition else value_if_false 在单行上选择两个值之一。它对于整齐的小选择很好,比如 "pass" if score >= 50 else "fail"。一旦逻辑超过那个,回到正常的 if 块,它读起来会更好。 while 循环
while 循环只要其条件为 True 就重复其块。当你不提前知道循环应该运行多少次时,使用它,比如等待有效输入或重试直到工作成功。
lives = 3
while lives > 0:
print(f"剩余生命:{lives}")
lives -= 1
print("游戏结束")while 在你不提前知道循环会运行多少次时最好用。当你知道时,或当你正在迭代集合时,for 更干净。
while 只要条件保持为真就继续运行其块。当你不知道需要多少轮时,比如等待有效输入时,使用它。确保循环内有东西朝着退出移动,否则它会永远运行,我不止一次犯过这个错误。 break 和 continue
break 立即退出循环,不管还有多少迭代。continue 跳过当前迭代的其余部分并跳回条件检查。两者都仅影响它们所在的最内层循环。
break 立即退出循环:
target = 5
num = 0
while True:
num += 1
if num == target:
print(f"找到 {target}")
break # 停止循环当退出条件复杂或需要在循环主体末尾发生时,while True: 加 break 是一个有效和常见的模式。
continue 跳过当前迭代的其余部分并回到条件检查:
num = 0
while num < 10:
num += 1
if num % 2 == 0:
continue # 跳过偶数
print(num) # 仅打印奇数:1, 3, 5, 7, 9break 立即跳出循环,continue 跳过这一轮的其余部分并回到检查条件。两者都仅触及它们直接在其中的循环。while True: 加 break 一开始读起来很奇怪,但它是循环直到某事发生的正常方式。 for 循环
for 循环逐个遍历序列中的项:列表、字符串、一系列数字。你在 for 后命名的变量依次接收每个项。你不需要管理计数器或自己检查长度。
players = ["王", "李", "张"]
for player in players:
print(f"你好,{player}!")for 循环也适用于字符串(逐个字符迭代)和任何其他序列类型。
for item in sequence 逐个遍历每个项,你永远不触及计数器或长度。它适用于列表、字符串、范围、很多东西。来自其他语言,不管理索引一开始感到陌生,然后它感到像一份礼物。 range()
range() 为你生成一个数字序列来循环。range(5) 给你 0, 1, 2, 3, 4。你可以控制开始、结束和步长。当你需要循环运行特定次数时,使用它。
for i in range(5):
print(i) # 0, 1, 2, 3, 4range() 有三种形式:
| 调用 | 产生什么 |
|---|---|
range(5) | 0, 1, 2, 3, 4 |
range(2, 6) | 2, 3, 4, 5 |
range(0, 10, 2) | 0, 2, 4, 6, 8(步长 2) |
range(5, 0, -1) | 5, 4, 3, 2, 1(倒计数) |
range() 不创建列表。它一个时间产生一个数字,这对于非常大的范围也是高效的。
range(5) 给你 0, 1, 2, 3, 4,你可以设置开始、停止和步长。当你想一个循环运行设定次数时使用它。它不构建列表,它一轮一个时间移交一个数字。 enumerate()
enumerate() 在你循环时同时给你索引和值,所以你不需要单独跟踪计数器。i, player 部分在每次迭代时自动接收一对值。
players = ["王", "李", "张"]
for i, player in enumerate(players):
print(f"{i + 1}. {player}")
# 1. 王
# 2. 李
# 3. 张i, player 语法称为解包。Python 自动将 (index, value) 对拆分为两个名称。
默认情况下 enumerate() 从 0 开始。传递一个起始值来改变它:
for i, player in enumerate(players, start=1):
print(f"{i}. {player}") # 从 1 开始enumerate() 一起给你位置和值,所以没有单独的计数器保持同步。i, player 部分一次捕捉两个。当你为有人读的列表编号时传递 start=1。 嵌套循环
你可以在另一个循环内放置一个循环。内循环对外循环的每一次迭代都完全运行。这是你处理网格、组合或任何具有两层结构的数据的方式。
rows = [1, 2, 3]
cols = ["A", "B"]
for row in rows:
for col in cols:
print(f"{col}{row}", end=" ")
print() # 每行后换行
# A1 B1
# A2 B2
# A3 B3嵌套循环内的 break 和 continue 仅影响最内层循环。
break 和 continue 仅作用于它们所坐的循环,这里是内循环。 循环-else
Python 循环可以有一个 else 子句,仅当循环完成而没有击中 break 时运行。它不常用,但它是编写"搜索列表,如果什么都没找到,做这个"的最干净方式。
target = "Dave"
names = ["Alice", "Bob", "Charlie"]
for name in names:
if name == target:
print(f"找到 {target}")
break
else:
print(f"{target} 不在列表中") # 运行因为 break 永不触发如果 break 运行,else 被跳过。如果循环耗尽序列,else 运行。它是一个利基模式但比标志变量更干净。
排序
sorted() 返回一个新的排序列表并保持原始列表不变。.sort() 就地排序列表并返回 None。key= 参数让你按原始值以外的东西排序。例如,区分大小写地排序名称或按分数排序玩家元组。
scores = [87, 42, 96, 55, 71]
ranked = sorted(scores) # [42, 55, 71, 87, 96](新列表)
scores.sort() # 排序原始列表,返回 None
scores.sort(reverse=True) # [96, 87, 71, 55, 42]两者都接受 key= 参数:一个在比较前应用于每个项的函数:
names = ["Charlie", "Alice", "Bob"]
sorted(names, key=str.lower) # 不区分大小写排序
players = [("Alice", 87), ("Bob", 96), ("Charlie", 55)]
sorted(players, key=lambda p: p[1]) # 按分数排序lambda p: p[1] 是一个单行函数:它取一个玩家元组并返回分数。Lambda 在Lambda 和推导章节中得到充分处理。
对于简单情况,使用 sorted()。对于你想就地修改的列表,使用 .sort()。
sorted() 移交一个新的排序列表并单独保留原始,.sort() 就地重新安排列表并返回 None。传递 key= 按原始值以外的东西排序,比如不区分大小写的名称。有疑问时,使用 sorted(),它是安全的。 实践中
循环通过分数,积累总计,计数及格成绩,并打印一个总结:
raw_scores = [87, 42, 96, 55, 71, 63]
total = 0
passing = 0
for score in raw_scores:
total += score
if score >= 60:
passing += 1
average = total / len(raw_scores)
print(f"平均:{average:.1f}")
print(f"及格:{passing}/{len(raw_scores)}")
print(f"最高分:{sorted(raw_scores, reverse=True)[0]}")
