本节书摘来自华章出版社《Python数据科学实践指南》一书中的第2章,第2.4节,纪路 著,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.4 流程控制
到目前为止,我们所编写的程序都是线性的程序,程序中的语句按照顺序依次被执行,这样的程序能实现的功能非常有限,而且还需要编写大量的代码,损失了编写程序执行任务的大部分优势。事实上,可以使用条件判断及循环这两种常用的方式更有效率地编写程序。
2.4.1 条件判断
带有条件判断的程序又称为“分支程序”,这样的程序由如三个部分构成。
一个条件判断,对一个表达式求值,结果是True或False。
一个代码块,如果条件判断为True,则执行这部分代码。
一个可选的代码块,如果条件判断为False,则执行这部分的代码。
这样的代码可以执行某些规则的比较,比如下面这段程序:
if sex == "man”:
print("he")
else:
print("her")
我们在性别分别为男或女时打印出不同的代词,当然也可以做数学运算,比如使用取模的方式x % 2 == 0来判断奇偶。还记得吗?“==”代表的是比较,求值之后的结果是布尔型的True或False,而“=”代表的是赋值,不要搞混这两个概念。
Python中另外一个重要的概念就是缩进。Python不像其他语言使用“;”或“{”、“}”来分割代码块,Python只使用一个缩进来区分语法块,比如上面代码中的第二行及第四行。虽然很多人对这种方式颇有微词,但是缩进处理的方法有一个好处,代码在视觉上和逻辑上的结构是完全一致的。比如下面这一段C代码:
if (friend_id != -1) {
if (m->friendlist[friend_id].status >= FRIEND_CONFIRMED)
return FAERR_ALREADYSENT;
}
这段代码是完全正确的,但是读者能够很轻易地区分出这段代码中的两个if是在同一个逻辑层级还是在不同的逻辑层级上。在Python中正确的缩进应该是像下面这样的:
if (friend_id != -1) {
if (m->friendlist[friend_id].status >= FRIEND_CONFIRMED)
return FAERR_ALREADYSENT;
}
这样阅读代码的时候逻辑确实更加清晰了。除了简单的if...else...语句之外,还有一个关键字“elif”,这个关键字是“else if”的缩写,我们可以写一个更加复杂的条件判断语句:
if x % 2 == 0:
if x % 3 == 0:
print "2和3的最小公倍数"
elif x % 3 == 0:
print "可以被3整除却不可以被2整除的数"
else:
print 0
在上面的程序中,elif语句的后面还可以写一个条件判断语句。需要注意的一点是,若elif语句之前的某个if或elif语句不成立, 就不会继续执行下去了,所以条件判断的顺序很重要。
虽然条件判断语句增强了我们编写程序的能力,但还有一个大问题需要解决,那就是如何让电脑任劳任怨地做我们为其安排的工作,这将会涉及2.2.2节将要介绍的流程控制方法。
2.4.2 循环
Python中的循环分为两种,让我们先从读者可能比较熟悉的while循环说起。与条件判断语句类似,while循环也是由条件判断语句和代码块构成的,示例代码如下:
x = 5
iters = 10
ans = 0
while iters > 0:
ans = ans + x
iters -= 1
print ans
上面的代码是计算5 * 10这个语句的复杂写法,先不要纠结这些细节,让我们来看看while循环需要哪些东西。这段程序的本质是将10个5加起来,所以需要循环10次,这一点可以从iters这个变量中确认,而变量ans则用于存储相加的总和。iters > 0是条件判断语句,当这个语句的结果为False时,这个循环就会终止。而程序块中一定要有一个语句用于减少iters的值,即iters -= 1这个语句,以保证循环最终会停止。我们可以在纸上手工计算这个程序,每一次迭代之后ans的值应该是:5,10,15,20,25,30,35,40,45,50。
如果忘记了增加iters -= 1会怎么样呢?结果就是这个程序永远都不会终止,直到整数溢出错误发生,所以请一定要多加注意。
Python的第二个循环方式是for循环,与其他编程语言一样,for循环是一种比while更简便的表达方式。虽然用while循环可以实现所有的循环,但是如果类似iters -= 1这样的语句忘记写了,或者写错了,就会发生死循环。而for循环在很多时候都能避免此类的情况发生,因为for循环实现的出发点就是循环次数是已知的。下面来看一个计算乘法的for循环版本:
x = 5
ans = 0
for iters in range(10):
ans = ans + x
print ans
这个程序中range()函数可以生成一个数列,遵循左闭右开的规则从0至9,让我们使用for循环打印出来查看一下:
>>> for iters in range(10):
... print iters
...
0
1
2
3
4
5
6
7
8
9
>>>
在for循环中,in后面一定是一个序列,然后在每一次循环中序列中的值都会依次赋值给iters,直到序列的最后一个值循环终止时为止。与序列的分片一样,range可以传入start、end、step三个参数,比如:
>>> for iters in range(5, 50, 5):
... print iters
...
5
10
15
20
25
30
35
40
45
>>>
上面的程序会生成一个从5起始,到50终止,步长为5的序列。如果要生成的序列过长,可以使用xrange()函数代替range()函数,xrange()函数是range()函数的生成器版本,可以在序列很长的时候节约内存。
与其他语言一样,Python的两种循环都支持continue和break语句,比如:
>>> x = 1
>>> ans = 0
>>> for iters in range(100):
... ans = ans + x
... if ans % 3 == 0:
... continue
... if ans >= 10:
... break
... print(ans)
...
1
2
4
5
7
8
>>>
在上面的程序中,第一个if中的continue会在ans能在被3整除的时候跳过当前这个循环,所以可以发现在输出结果中3、6和9不见了。而第二个if语句在使用break语句时如果ans大于10就会跳过整个循环程序块,即使循环次数没有达到100次也会跳出。
2.4.3 缩进、空白和注释
Python中不需要用“;”和“{}”来对代码块进行分割,而是使用缩进来进行分割。有些初学者在使用文本编辑器时往往没有注意空格缩进与tab的区别,导致程序执行出错,这是需要注意的。而且一旦决定分隔符的空格数就要一直使用相同的空格数作为缩进,通常来说,Python官方建议使用4个空格作为分隔符。不幸的是,Sublime Text 3的默认分割符是tab,读者可以尝试在Performance>Settings-User打开的文件中插入两行,以确保换行之后自动插入4个空格的缩进:
{
"tab_size": 4,
"translate_tabs_to_spaces": true
}
在Python中,空白行是没有任何意义的,用来保证美观即可。通常来说,逻辑上无关的程序块之间需要用两个空行进行分割。注释同样没有意义,通常以“#”开头。多行注释可以用来进行大段的描述,使用三引号进行表达,下面就列出几种常见的Python注释。
单行注释:
# Make sure the instance knows which cache to remove itself from.
多行注释:
"""A blocking HTTP client.
This interface is provided for convenience and testing; most applications
that are running an IOLoop will want to use `AsyncHTTPClient` instead.
Typical usage looks like this::
"""