2-2-if-测试和语法规则

1. if 语句

1.1 通用格式

if,后面跟一个或多个可选的 elif(“else if”),以及一个最终可选的 else 块。

一般形式如下:

1
2
3
4
5
6
if <test1>:
<statements1>
elif <test2>:
<statements2>
else:
<statements3>

1.2 基本例子

1
2
if 1:
print('true')
true

1.3 多路分支

1
2
3
4
5
6
7
x = 'killer rabbit'
if x == 'roger':
print("how's jessica?")
elif x == 'bugs':
print("what's up doc?")
else:
print('Run away! Run away!')
Run away! Run away!

elif 和 else 部分都可以省略,而且每一段中可以嵌套一个以上的语句。

Python 中没有 switch 或 case 语句,多路分支是写成一系列的 if/elif 测试,或者对字典进行索引运算或搜索列表。

因为字典和列表可在运行时创建,有时会比硬编码的 if 逻辑更有灵活性

1
2
3
4
5
choice = 'ham'
print({'spam': 1.25, # 基于字典的 'switch'
'ham': 1.99, # 默认使用 get
'eggs': 0.99,
'bacon': 1.10}[choice])
1.99

这个字典是多路分支:根据键的选择进行索引,再分支到一组值的其中一个,很像 C 语言的 switch。

字典默认值能编码到 get 方法调用或异常捕捉中,在字典式的多路分支中也可用这些技术编写默认动作:

1
2
3
4
branch = {'spam': 1.25,
'ham': 1.99,
'eggs': 0.99}
print(branch.get('spam', 'Bad choice'))
1.25
1
print(branch.get('bacon', 'Bad choice'))
Bad choice

位于一条 if 语句中的 in 成员测试也有同样的效果:

1
2
3
4
5
choice = 'bacon'
if choice in branch:
print(branch[choice])
else:
print('Bad choice')
Bad choice

try 语句是一种通过捕获和处理异常来处理默认值的通用方法:

1
2
3
4
try:
print(branch[choice])
except KeyError:
print('Bad choice')
Bad choice

虽然字典式多路分支在处理动态数据的程序中很有用,但编写 if 语句是执行多路分支最直接的方式。

编写代码时的原则是:有疑虑的时候,就遵循简易性原则和可读性原则。

2. Python 语法规则

  • 语句是逐个运行的,除非你不这样编写。
  • 块和语句的边界会自动检测。
  • 复合语句=首行+“:”+缩进语句。
  • 空白行、空格以及注释通常都会忽略。
  • 文档字符串(docstring)会忽略,但会保存并由工具显示。

2.1 代码块分隔符

Python 会自动以行缩进检测块的边界,缩进至右侧相同距离的所有语句属于同一块的代码。换句话说,块内的语句会垂直对齐,就好像在一栏之内。块会在文件末尾或者碰到缩进量较少的行时结束,而更深层的嵌套块就是比所在块的语句进一步向右缩进。

1
2
3
4
5
6
7
x = 1
if x:
y = 2
if y:
print('block2')
print('block1')
print('block0')

通常来说,顶层(无嵌套)代码必须位于第一栏开始,嵌套块可以从任何栏开始,缩进可以由任意的空格和制表符组成,只要特定的单个块中的所有语句都相同即可。

避免混合使用制表符和空格。

2.2 语句的分隔符

Python 的语句一般都是在其所在行的末尾结束的,不过,当语句太长、难以单放在一行时,有些特殊的规则可使其位于多行之中。

  • **如果使用语法括号对,语句就可以横跨数行。**如果在封闭的()、{}或[]这类配对中编写代码,Python就可以让你在下一行继续输入语句。
  • **如果语句以反斜线结尾,就可以横跨数行。**这是有点过时的功能,但是如果语句需要横跨数行,也可以在前一行的末尾加上反斜线(\),以表示要在下一行继续输入。因为可以在较长结构两侧加上括号以便继续输入,反斜线几乎都已经不再使用。这种方法容易导致错误:偶尔忘掉一个 \ 通常会产生语法错误。
  • **字符串常量有特殊规则。**三重引号字符串块可以横跨数行。相邻字符串常量是隐式地连接起来的。
  • **其他规则。**可以用分号终止语句:这种惯例有时用于把一个以上的简单(非复合)语句挤进单个的行中。注释和空白行能出现在文件的任意之处。注释(以#字符开头)则在其出现的行的末尾终止。

2.3 一些特殊情况

1
2
3
L = ['Good',
'Bad',
'Ugly'] # 括号可以横跨数行

括号可以存放表达式、函数参数、函数的首行、元组和生成器表达式,以及可以放到花括号中的任何内容。

如果程序代码需要横跨数行,通常可以改用开放对技术——直接把语句的部分包含在圆括号中:

1
2
3
if (a == b and c == d and
d == e and e == f):
print('new')

Python 允许在相同行上编写一个以上的非复合语句,由分号隔开:

1
x = 1; y = 2; print(x)

如果两个字符串常量彼此相邻地出现,它们会合并,就好像它们之间已经放置了一个 + ——当和开放对规则一起使用时,包括在圆括号中就允许这种形式跨越多行。

1
2
3
4
5
S = """
aaa
bbb
ccc"""
print(S)
aaa
bbb
ccc
1
2
3
4
S = ('aaa'
'bbb' # 注释将被忽略
'ccc')
print(S)
aaabbbccc

上面第一个示例在换行处插入换行字符,并且把 '\naaa\nbbb\nccc' 赋给S,而第二个示例隐式地合并,并且把 S 赋值为 aaabbbccc

Python 可把复合语句的主体上移到首行,只要该主体只是简单语句:

1
if 1: print('hello')
hello

3. 真值和布尔值测试

Python 的布尔运算符和 C 这类语言的布尔运算符有些不同,在 Python 中:

  • 所有对象本质上不是真就是假。
  • 任何非零数字或非空对象都为真。
  • 数字零、空对象以及特殊对象 None 都被认作是假。
  • 比较和相等测试会递归地应用在数据结构中。
  • 比较和相等测试会返回 True 或 False(1 和 0 的特殊版本)。
  • 布尔 and 和 or 运算符会返回真或假的操作对象。
  • 一旦求出结果,布尔运算符就停止计算(“短路”)

布尔运算符是用于结合其他测试的结果,Python 中有三种布尔表达式运算符:

  • X and Y
  • X or Y
  • not X

此处,X 和 Y 可以是任何真值或者返回真值的表达式(例如,相等测试、范围比较等)。

Python 中值的比较会返回 True 或 False 作为其真值结果。

1
2 < 3, 3 < 2           # 小于:返回 True 或 False
(True, False)

and 和 or 运算符总会返回对象,不是运算符左侧的对象,就是右侧的对象。

就 or 测试而言,Python 会由左至右求算操作对象,然后返回第一个为真的操作对象,Python 会在其找到的第一个真值操作数的地方停止,因为求出结果后,就会使表达式其余部分短路(终止):

1
2 or 3, 3 or 2          # 如果真就返回左侧操作数,否则返回右侧操作数(右侧操作数可能是真或假)
(2, 3)
1
[] or 3
3
1
[] or {}
{}

在求出结果时,and 运算也会立刻停止。就此而言,Python 由左至右计算操作数,并且停在第一个为假的对象上,因为它决定了结果——false and 任意对象的结果总是 false:

1
2 and 3, 3 and 2
(3, 2)
1
[] and {}
[]
1
3 and []
[]

4. if/else 三元表达式

1
2
3
4
if X:
A = Y
else:
A = Z

有时这类语句中涉及的元素相当简单,用四行代码编写似乎太浪费了。Python 2.5 引入了新的表达式格式,让我们可以在一个表达式中编写出相同的结果:

1
A = Y if X else Z

只有当 X 为真,Python 才会执行表达式 Y,只有当 X 为假,才会执行表达式 Z。这就是短路运算,就像布尔运算符一样的行为。

1
2
A = 't' if 'spam' else 'f'              # 非空即为真
A
't'
1
2
A = 't' if '' else 'f'
A
'f'