4. PY4E - Python 为所有人¶
切换导航
第 1 章:简介 第 2 章:变量 第 3 章:条件语句 第 4 章:函数 第 5 章:迭代 第 6 章:字符串 第 7 章:文件 第 8 章:列表 第 9 章:字典 第 10 章:元组 第 11 章:正则表达式 第 12 章:网络程序 第 13 章:Python 与 Web 服务 第 14 章:Python 对象 第 15 章:Python 与数据库 第 16 章:数据可视化
4.1. 条件执行¶
4.1.1. 布尔表达式¶
一个 布尔表达式 是一个结果为真或假的表达式。以下示例使用运算符 ==,该运算符比较两个操作数,若它们相等则产生 True,否则产生 False:
>>> 5 == 5
True
>>> 5 == 6
False
True 和 False 是属于类 bool 的特殊值;它们不是字符串:
>>> type(True)
<class 'bool'>
>>> type(False)
<class 'bool'>
== 运算符是 比较运算符 之一;其他比较运算符包括:
x != y # x is not equal to y
x > y # x is greater than y
x < y # x is less than y
x >= y # x is greater than or equal to y
x <= y # x is less than or equal to y
x is y # x is the same as y
x is not y # x is not the same as y
尽管这些操作可能对您来说很熟悉,但 Python 符号与相同操作的数学符号不同。一个常见的错误是使用单个等号 (=) 而不是双等号 (==)。请记住,= 是赋值运算符,而 == 是比较运算符。不存在 =< 或 => 这样的东西。
4.1.2. 逻辑运算符¶
存在三个 逻辑运算符:and、or``和``not。这些运算符的语义(含义)与它们在英语中的含义相似。例如,
x > 0 and x < 10
仅当 x 大于 0 且小于 10 时,该条件才为真。
n%2 == 0 or n%3 == 0 为真,如果 either 两个条件之一为真,即如果该数字能被 2 or 3 整除。
最后,not 运算符对布尔表达式取反,因此如果 x > y 为假,则 not (x > y) 为真;也就是说,如果 x 小于或等于 y。
严格来说,逻辑运算符的操作数应为布尔表达式,但 Python 并不十分严格。任何非零数字都被解释为“真”。
>>> 17 and True
True
这种灵活性可能很有用,但其中存在一些微妙之处可能会令人困惑。在你确定自己知道自己在做什么之前,你可能想避免使用它。
4.1.3. 条件执行¶
为了编写有用的程序,我们几乎总是需要具备检查条件并根据相应情况改变程序行为的能力。条件语句 赋予了我们这种能力。最简单的形式是 if 语句:
if x > 0 :
print('x is positive')
在 `if` 语句之后的布尔表达式称为 condition。我们在 `if` 语句后添加冒号字符 (:),并且 if 语句之后的行需要缩进。
如果逻辑
如果逻辑条件为真,则执行缩进的语句。如果逻辑条件为假,则跳过缩进的语句。
if 语句具有与函数定义或 for 循环:sup:`1 <https://www.py4e.com/html3/03-conditional#fn1>`__` 相同的结构。该语句由一个以冒号字符 (:) 结尾的标题行和一个缩进块组成。此类语句被称为*复合语句*,因为它们跨越多行。
语句体中出现的语句数量没有限制,但必须至少有一条。偶尔,拥有一个没有语句的语句体是有用的(通常作为尚未编写的代码的占位符)。在这种情况下,你可以使用 pass 语句,它什么都不做。
if x < 0 :
pass # need to handle negative values!
如果在 Python 解释器中输入一条 `if` 语句,提示符将从三个尖括号变为三个点,以表明您正处于一组语句的中间,如下所示:
>>> x = 3
>>> if x < 10:
... print('Small')
...
Small
>>>
使用 Python 解释器时,必须在块末尾留出一个空行,否则 Python 将返回错误:
>>> x = 3
>>> if x < 10:
... print('Small')
... print('Done')
File "<stdin>", line 3
print('Done')
^
SyntaxError: invalid syntax
在编写和执行脚本时,语句块末尾的空行并非必需,但它可以提高代码的可读性。
4.1.4. 替代执行¶
另一种形式的 if 语句是 替代执行,其中存在两种可能性,条件决定执行哪一种。语法如下:
if x%2 == 0 :
print('x is even')
else :
print('x is odd')
如果 x 除以 2 的余数为 0,则我们知道 x 是偶数,程序会显示一条相应的消息。如果条件为假,则执行第二组语句。
如果 - 那么 - 否则逻辑
由于条件必须为真或假,因此将执行两个替代方案中的恰好一个。这些替代方案被称为 分支,因为它们是执行流程中的分支。
4.1.5. 链式条件判断¶
有时会有不止两种可能性,我们需要不止两个分支。表达此类计算的一种方法是使用 链式条件判断:
if x < y:
print('x is less than y')
elif x > y:
print('x is greater than y')
else:
print('x and y are equal')
elif 是"else if"的缩写。同样,恰好会执行一个分支。
如果 - 则 - 否则如果逻辑
elif 语句的数量没有限制。如果有 else 子句,它必须位于最后,但并非必须存在。
if choice == 'a':
print('Bad guess')
elif choice == 'b':
print('Good guess')
elif choice == 'c':
print('Close, but not correct')
每个条件按顺序进行检查。如果第一个为假,则检查下一个,依此类推。如果其中一个为真,则执行相应的分支,语句结束。即使有多个条件为真,也仅执行第一个为真的分支。
4.1.6. 嵌套条件语句¶
一个条件语句也可以嵌套在另一个条件语句中。我们可以像这样编写三分支示例:
if x == y:
print('x and y are equal')
else:
if x < y:
print('x is less than y')
else:
print('x is greater than y')
外层条件包含两个分支。第一个分支包含一个简单的语句。第二个分支包含另一个 if 语句,该语句本身也有两个分支。这两个分支都是简单的语句,尽管它们也可以是条件语句。
嵌套 if 语句
尽管语句的缩进使结构显而易见,但 嵌套条件语句 很快就会变得难以阅读。一般来说,只要可能,最好避免使用它们。
逻辑运算符通常提供了一种简化嵌套条件语句的方法。例如,我们可以使用单个条件语句重写以下代码:
if 0 < x:
if x < 10:
print('x is a positive single-digit number.')
The print 语句仅在通过两个条件判断后才执行,因此我们可以使用 and 运算符获得相同的效果:
if 0 < x and x < 10:
print('x is a positive single-digit number.')
4.1.7. 使用 try 和 except 捕获异常¶
之前我们曾看到一段代码,其中使用了 `input` 和 `int` 函数来读取并解析用户输入的一个整数。我们也看到了这样做是多么危险:
>>> prompt = "What is the air velocity of an unladen swallow?\n"
>>> speed = input(prompt)
What is the air velocity of an unladen swallow?
What do you mean, an African or a European swallow?
>>> int(speed)
ValueError: invalid literal for int() with base 10:
>>>
当我们在 Python 解释器中执行这些语句时,我们会从解释器获得一个新的提示符,心想“哎呀”,然后继续执行下一条语句。
然而,如果您将此代码放在 Python 脚本中且出现此错误,脚本会立即停止运行并显示回溯信息。它不会执行下一条语句。
下面是一个将华氏温度转换为摄氏温度的示例程序:
inp = input('Enter Fahrenheit Temperature: ')
fahr = float(inp)
cel = (fahr - 32.0) * 5.0 / 9.0
print(cel)
# Code: http://www.py4e.com/code3/fahren.py
如果我们执行此代码并给它无效的输入,它仅会以不友好的错误消息失败:
python fahren.py
Enter Fahrenheit Temperature:72
22.22222222222222
python fahren.py
Enter Fahrenheit Temperature:fred
Traceback (most recent call last):
File "fahren.py", line 2, in <module>
fahr = float(inp)
ValueError: could not convert string to float: 'fred'
Python 中内置了一种用于处理此类预期和意外错误的条件执行结构,称为“try / except"。try 和 except 的含义是,你知道某些指令序列可能会出现错误,并希望添加一些在发生错误时执行的语句。如果没有错误,这些额外的语句(except 块)将被忽略。
在 Python 中,你可以将 try 和 except 特性视为一系列语句的“保险单”。
我们可以将我们的温度转换器重写如下:
inp = input('Enter Fahrenheit Temperature:')
try:
fahr = float(inp)
cel = (fahr - 32.0) * 5.0 / 9.0
print(cel)
except:
print('Please enter a number')
# Code: http://www.py4e.com/code3/fahren2.py
Python 首先执行 try 块中的语句序列。如果一切顺利,它会跳过 except 块并继续执行。如果在 try 块中发生异常,Python 会跳出 try 块并执行 except 块中的语句序列。
python fahren2.py
Enter Fahrenheit Temperature:72
22.22222222222222
python fahren2.py
Enter Fahrenheit Temperature:fred
Please enter a number
处理带有 try 语句的异常称为捕获异常。在此示例中,except 子句打印错误消息。通常,捕获异常为您提供机会来修复问题,或重试,或至少优雅地结束程序。
4.1.8. 逻辑表达式的短路求值¶
当 Python 处理逻辑表达式如 x >= 2 and (x/y) > 2 时,它会从左到右评估该表达式。由于 and 的定义,如果 x 小于 2,则表达式 x >= 2 为 False,因此整个表达式为 False,无论 (x/y) > 2 是否评估为 True 或 False。
当 Python 检测到评估逻辑表达式的其余部分无法获益时,它会停止评估,并不执行逻辑表达式其余部分的计算。当逻辑表达式的评估因整体值已确定而停止时,这被称为 短路 评估。
虽然这看似是一个细微之处,但短路行为导致了一种称为 guardian pattern 的技巧。考虑以下在 Python 解释器中的代码序列:
>>> x = 6
>>> y = 2
>>> x >= 2 and (x/y) > 2
True
>>> x = 1
>>> y = 0
>>> x >= 2 and (x/y) > 2
False
>>> x = 6
>>> y = 0
>>> x >= 2 and (x/y) > 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>>
之前的计算失败是因为 Python 评估了 (x/y) 且 y 为零,这会导致运行时错误。但第一个和第二个示例并未失败,因为在第一次计算中 y 非零,而在第二次计算中这些表达式的第一部分 x >= 2 评估为 False,因此由于 short-circuit 规则,(x/y) 从未被执行,也就没有发生错误。
我们可以构造逻辑表达式,以便在可能导致错误的评估之前战略性地放置一个 守卫 评估,如下所示:
>>> x = 1
>>> y = 0
>>> x >= 2 and y != 0 and (x/y) > 2
False
>>> x = 6
>>> y = 0
>>> x >= 2 and y != 0 and (x/y) > 2
False
>>> x >= 2 and (x/y) > 2 and y != 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>>
在第一个逻辑表达式中,x >= 2 是 False,因此评估在 and 处停止。在第二个逻辑表达式中,x >= 2 是 True 但 y != 0 是 False,因此我们从未到达 (x/y)。
在第三个逻辑表达式中,y != 0 在 (x/y) 计算之后才进行,因此该表达式会因错误而失败。
在第二个表达式中,我们说 y != 0 充当 guard,以确保仅在 y 非零时才执行 (x/y)。
4.1.9. 调试¶
当发生错误时,Python 显示的堆栈跟踪(traceback)包含大量信息,但可能会令人不知所措。最有用的部分通常是:
发生了何种错误,以及
它发生的位置。
语法错误通常很容易发现,但有几个需要注意的地方。空白字符错误可能很棘手,因为空格和制表符是不可见的,而我们习惯于忽略它们。
>>> x = 5
>>> y = 6
File "<stdin>", line 1
y = 6
^
IndentationError: unexpected indent
在此示例中,问题在于第二行缩进了一个空格。但错误信息指向 y,这具有误导性。通常,错误信息指示问题被发现的位置,但实际错误可能出现在代码的更早位置,有时甚至在前一行。
通常情况下,错误消息会告诉你问题是在哪里被发现的,但那里往往不是问题产生的地方。
4.1.10. 术语表¶
body 复合语句内的语句序列。boolean expression 值为 True 或 False 的表达式。branch 条件语句中的替代语句序列之一。chained conditional 具有一系列替代分支的条件语句。comparison operator 用于比较操作数的运算符之一:==、!=、>、<、>= 和 <=。conditional statement 根据某些条件控制执行流程的语句。condition 条件语句中决定执行哪个分支的布尔表达式。compound statement 由标题和主体组成的语句。标题以冒号 (:) 结尾。主体相对于标题进行缩进。guardian pattern 我们构建带有额外比较的逻辑表达式,以利用短路行为。logical operator 组合布尔表达式的运算符之一:and、or 和 not。nested conditional 出现在另一个条件语句的某个分支中的条件语句。traceback 执行中的函数列表,在发生异常时打印。short circuit 当 Python 正在评估逻辑表达式的一部分时,因 Python 知道无需评估剩余表达式即可得出表达式的最终值而停止评估。
4.1.11. 练习¶
练习 1:重写你的工资计算程序,使超过 40 小时的工作时间按每小时工资的 1.5 倍计算。
Enter Hours: 45
Enter Rate: 10
Pay: 475.0
练习 2:使用 ``try`` 和 ``except`` 重写你的工资程序,使你的程序能够优雅地处理非数字输入,通过打印一条消息并退出程序。以下展示了该程序的两次运行:
Enter Hours: 20
Enter Rate: nine
Error, please enter numeric input
Enter Hours: forty
Error, please enter numeric input
练习 3:编写一个程序,提示用户输入 0.0 到 1.0 之间的分数。如果分数超出范围,则打印错误信息。如果分数在 0.0 到 1.0 之间,则根据下表打印等级:
Score Grade
>= 0.9 A
>= 0.8 B
>= 0.7 C
>= 0.6 D
< 0.6 F
Enter score: 0.95
A
Enter score: perfect
Bad score
Enter score: 10.0
Bad score
Enter score: 0.75
C
Enter score: 0.5
F
重复运行该程序如上所示,以测试各种不同的输入值。
我们将在第 4 章学习函数,在第 5 章学习循环。↩︎
如果您在本书中发现错误,欢迎使用 Github 发送修正。
