一、条件语句
Python 的条件语句,用法很简单,但需要注意 Python 的缩进规则。常见的用法分别是:单分支、双分支和多分支。对应语法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 单分支; if <expression>: <statement> # 双分支; if <expression>: <statement> else: <statement> # 多分支; if <expression>: <statement> elif <expression>: <statement> elif ...: <statement> else: <statement> |
由于 Python 不支持 switch 语句,因此,当存在多个条件判断时,我们需要用 else if 来实现,这在 Python 中的表达是 elif。
但需要注意的是,在条件语句的末尾必须加上冒号(:),这是 Python 特定的语法规范。
整个条件语句是顺序执行的,如果遇到一个条件满足,在执行完 statement 后,便会退出整个 if、elif、else 条件语句,而不会继续向下执行。
这个语句在工作中很常用,比如下面的这个例子。输入用户年龄,根据年龄打印不同的内容,在 Python 程序中,用if
语句实现:
1 2 3 4 |
age = 20 if age >= 18: print('your age is', age) print('adult') |
也可以给if
添加一个else
语句,意思是,如果if
判断是False
,不要执行if
的内容,去把else
执行了:
1 2 3 4 5 6 7 |
age = 3 if age >= 18: print('your age is', age) print('adult') else: print('your age is', age) print('teenager') |
当然上面的判断是很粗略的,完全可以用elif
做更细致的判断:
1 2 3 4 5 6 7 |
age = 3 if age >= 18: print('adult') elif age >= 6: print('teenager') else: print('kid') |
if
判断条件还可以省略判断条件,比如写:
1 2 3 |
x = 1 if x: print('True') |
只要x
是非零数值、非空字符串、非空list等,就判断为True
,否则为False
。
关于省略判断条件的常见用法,我大概总结了一下:
判断条件的省略用法 | |
数据类型 | 结果 |
string | 空字符串解析为False,其余为True |
int | 0解析为False,其余为True |
bool | True为True,False为False |
list/dict/tuple/set | Iterable为空解析为False,其余为True |
object | None解析为False,其余为True |
不过,切记,在实际写代码时,我们鼓励,除了 boolean 类型的数据,条件判断最好是显性的。比如,在判断一个整型数是否为 0 时,我们最好写出判断的条件:
1 2 |
if i != 0: ... |
而不是只写出变量名:
1 2 |
if i: ... |
二、循环语句
讲完了条件语句,我们接着来看循环语句。所谓循环,顾名思义,本质上就是遍历集合中的元素。和其他语言一样,Python 中的循环一般通过 for 循环和 while 循环实现。
比如,我们有一个列表,需要遍历列表中的所有元素并打印输出,代码如下:
1 2 3 4 5 6 7 |
l = [1, 2, 3, 4] for item in l: print(item) 1 2 3 4 |
其实,Python 中的数据结构只要是可迭代的(iterable),比如列表、集合等等,那么都可以通过下面这种方式遍历:
1 2 |
for item in <iterable>: ... |
这里需要单独强调一下字典。字典本身只有键是可迭代的,如果我们要遍历它的值或者是键值对,就需要通过其内置的函数 values() 或者 items() 实现。其中,values() 返回字典的值的集合,items() 返回键值对的集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
d = {'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'} for k in d: # 遍历字典的键 print(k) name dob gender for v in d.values(): # 遍历字典的值 print(v) jason 2000-01-01 male for k, v in d.items(): # 遍历字典的键值对 print('key: {}, value: {}'.format(k, v)) key: name, value: jason key: dob, value: 2000-01-01 key: gender, value: male |
看到这里你也许会问,有没有办法通过集合中的索引来遍历元素呢?当然可以,其实这种情况在实际工作中还是很常见的,甚至很多时候,我们还得根据索引来做一些条件判断。
我们通常通过 range() 这个函数,拿到索引,再去遍历访问集合中的元素。比如下面的代码,遍历一个列表中的元素,当索引小于 5 时,打印输出:
1 2 3 4 5 6 7 8 9 10 |
l = [1, 2, 3, 4, 5, 6, 7] for index in range(0, len(l)): if index < 5: print(l[index]) 1 2 3 4 5 |
当我们同时需要索引和元素时,还有一种更简洁的方式,那就是通过 Python 内置的函数 enumerate()。用它来遍历集合,不仅返回每个元素,并且还返回其对应的索引,这样一来,上面的例子就可以写成:
1 2 3 4 5 6 7 8 9 10 |
l = [1, 2, 3, 4, 5, 6, 7] for index, item in enumerate(l): if index < 5: print(item) 1 2 3 4 5 |
在循环语句中,我们还常常搭配 continue 和 break 一起使用。所谓 continue,就是让程序跳过当前这层循环,继续执行下面的循环;而 break 则是指完全跳出所在的整个循环体。在循环中适当加入 continue 和 break,往往能使程序更加简洁、易读。
比如,给定两个字典,分别是产品名称到价格的映射,和产品名称到颜色列表的映射。我们要找出价格小于 1000,并且颜色不是红色的所有产品名称和颜色的组合。如果不用 continue,代码应该是下面这样的:
1 2 3 4 5 6 7 8 9 10 |
# name_price: 产品名称 (str) 到价格 (int) 的映射字典 # name_color: 产品名字 (str) 到颜色 (list of str) 的映射字典 for name, price in name_price.items(): if price < 1000: if name in name_color: for color in name_color[name]: if color != 'red': print('name: {}, color: {}'.format(name, color)) else: print('name: {}, color: {}'.format(name, 'None')) |
而加入 continue 后,代码显然清晰了很多:
1 2 3 4 5 6 7 8 9 10 11 12 |
# name_price: 产品名称 (str) 到价格 (int) 的映射字典 # name_color: 产品名字 (str) 到颜色 (list of str) 的映射字典 for name, price in name_price.items(): if price >= 1000: continue if name not in name_color: print('name: {}, color: {}'.format(name, 'None')) continue for color in name_color[name]: if color == 'red': continue print('name: {}, color: {}'.format(name, color)) |
我们可以看到,按照第一个版本的写法,从开始一直到打印输出符合条件的产品名称和颜色,共有 5 层 for 或者 if 的嵌套;但第二个版本加入了 continue 后,只有 3 层嵌套。
显然,如果代码中出现嵌套里还有嵌套的情况,代码便会变得非常冗余、难读,也不利于后续的调试、修改。因此,我们要尽量避免这种多层嵌套的情况。
前面讲了 for 循环,对于 while 循环,原理也是一样的。它表示当 condition 满足时(True),一直重复循环内部的操作,直到 condition 不再满足(False),就跳出循环体。如果条件一直满足,循环将会无限的执行下去。
1 2 3 4 |
while <expression>: statement else: statement |
while 语句时还有另外两个重要的命令 continue,break 来跳过循环。continue 用于跳过该次循环,break 则是用于退出循环,此外”判断条件”还可以是个常值,表示循环必定成立。
在 Python 中,while … else 在循环条件为 False 时执行 else 语句块,其中 else 块可用可不用。如果用了,只有当循环正常终止,else 才会执行;如果循环终止是由 break 跳出导致的则 else 不会执行。
很多时候,for 循环和 while 循环可以互相转换,比如要遍历一个列表,我们用 while 循环同样可以完成:
1 2 3 4 5 |
l = [1, 2, 3, 4] index = 0 while index < len(l): print(l[index]) index += 1 |
那么,两者的使用场景又有什么区别呢?
通常来说,如果你只是遍历一个已知的集合,找出满足条件的元素,并进行相应的操作,那么使用 for 循环更加简洁。但如果你需要在满足某个条件前,不停地重复某些操作,并且没有特定的集合需要去遍历,那么一般则会使用 while 循环。
比如,某个交互式问答系统,用户输入文字,系统会根据内容做出相应的回答。为了实现这个功能,我们一般会使用 while 循环,大致代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
while True: try: text = input('Please enter your questions, enter "q" to exit') if text == 'q': print('Exit system') break ... ... print(response) except as err: print('Encountered error: {}'.format(err)) break |
同时需要注意的是,for 循环和 while 循环的效率问题。比如下面的 while 循环:
1 2 3 |
i = 0 while i < 1000000: i += 1 |
和等价的 for 循环:
1 2 |
for i in range(0, 1000000): pass |
究竟哪个效率高呢?
要知道,range() 函数是直接由 C 语言写的,调用它速度非常快。而 while 循环中的“i += 1”这个操作,得通过 Python 的解释器间接调用底层的 C 语言;并且这个简单的操作,又涉及到了对象的创建和删除(因为 i 是整型,是 immutable,i += 1 相当于 i = new int(i + 1))。所以,显然,for 循环的效率更胜一筹。
三、条件与循环的复用
前面两部分讲了条件与循环的一些基本操作,接下来,我们重点来看它们的进阶操作,让程序变得更简洁高效。
在阅读代码的时候,你应该常常会发现,有很多将条件与循环并做一行的操作,例如:
1 |
expression1 if condition else expression2 for item in iterable |
将这个表达式分解开来,其实就等同于下面这样的嵌套结构:
1 2 3 4 5 |
for item in iterable: if condition: expression1 else: expression2 |
而如果没有 else 语句,则需要写成:
1 |
expression for item in iterable if condition |
举个例子,我们在处理文件中的字符串时,常常遇到的一个场景:将文件中逐行读取的一个完整语句,按逗号分割单词,去掉首位的空字符,并过滤掉长度小于等于 5 的单词,最后返回由单词组成的列表。这同样可以简洁地表达成一行:
1 2 3 4 |
text = ' Today, is, Sunday' text_list = [s.strip() for s in text.split(',') if len(s.strip()) > 5] print(text_list) ['Today', 'Sunday'] |
当然,这样的复用并不仅仅局限于一个循环。比如,给定两个列表 x、y,要求返回 x、y 中所有元素对组成的元祖,相等情况除外。那么,你也可以很容易表示出来:
1 |
[(xx, yy) for xx in x for yy in y if xx != yy] |
这样的写法就等价于:
1 2 3 4 5 |
l = [] for xx in x: for yy in y: if xx != yy: l.append((xx, yy)) |
熟练之后,你会发现这种写法非常方便。当然,如果遇到逻辑很复杂的复用,你可能会觉得写成一行难以理解、容易出错。那种情况下,用正常的形式表达,也不失为一种好的规范和选择。