Python 核心编程(第二版)——条件和循环
Python 中的 if 子句由三部分组成: 关键字本身,用于判断结果真假的条件表达式, 以及当表达式为真或者非零时执行的代码块。if 语句的语法如下:
if expression:
expr_true_suite
单个 if 语句可以通过使用布尔操作符 and , or 和 not实现多重判断条件或是否定判断条件。
如果一个复合语句(例如 if 子句, while 或 for 循环)的代码块仅仅包含一行代码, 那么它可以和前面的语句写在同一行上:if make_hard_copy: send_data_to_printer()。
2.else语句
如果 if 语句的条件表达式的结果布尔值为假, 那么程序将执行 else 语句后的代码:
if expression:
expr_true_suite
else:
expr_false_suite
Python 的缩进使用强制使代码正确对齐, 让程序员来决定 else 属于哪一个 if . 限制您的选择从而减少了不确定性。
3.elif(即else-if)语句
elif 是 Python 的 else-if 语句, 它检查多个表达式是否为真, 并在为真时执行特定代码块中的代码。if 语句后最多只能有一个 else语句, 但可以有任意数量的 elif 语句。
if expression1:
expr1_true_suite
elif expression2:
expr2_true_suite
elif expressionN:
exprN_true_suite
else:
none_of_the_above_suite
4.条件表达式(即“三元操作符”)
语法为:X if C else Y ,C为条件表达式,X 是 C 为 True 时的结果, Y 是 C 为 False 时的结果。
5.while语句
while 循环的语法如下:
while expression:
suite_to_repeat
while 循环的 suite_to_repeat 子句会一直循环执行,直到 expression 值为布尔假。
while True:#无限循环
suite_to_repeat
6.for语句
for 循环会访问一个可迭代对象(例如序列或是迭代器)中的所有元素, 并在所有条目都处理过后结束循环。它的语法如下:
for iter_var in iterable:
suite_to_repeat
每次循环,iter_var 迭代变量被设置为可迭代对象(序列, 迭代器, 或者是其他支持迭代的对象)的当前元素, 提供给 suite_to_repeat 语句块使用。
迭代序列有三种基本方法:
1)通过序列项迭代;
1 >>> nameList = ['Walter', "Nicole", 'Steven', 'Henry'] 2 >>> for eachName in nameList: 3 ... print eachName, "Lim" 4 ... 5 Walter Lim 6 Nicole Lim 7 Steven Lim 8 Henry Lim
2)通过序列索引迭代;
1 >>> nameList = ['Cathy', "Terry", 'Joe', 'Heather', 2 'Lucy'] 3 >>> for nameIndex in range(len(nameList)): 4 ... print "Liu,", nameList[nameIndex] 5 ... 6 Liu, Cathy 7 Liu, Terry 8 Liu, Joe 9 Liu, Heather 10 Liu, Lucy
使用 range() 我们可以得到用来迭代 nameList 的索引数列表;使用切片/下标操作符( [ ] ),就可以访问对应的序列对象。
3)使用项和索引迭代;
1 >>> nameList = ['Donn', 'Shirley', 'Ben', 'Janice', 2 ... 'David', 'Yen', 'Wendy'] 3 >>> for i, eachLee in enumerate(nameList): 4 ... print "%d %s Lee" % (i+1, eachLee) 5 ... 6 1 Donn Lee 7 2 Shirley Lee 8 3 Ben Lee 9 4 Janice Lee 10 5 David Lee 11 6 Yen Lee 12 7 Wendy Lee
Python 提供了两种不同的方法来调用 range() . 完整语法要求提供两个或三个整数参数:
range(start, end, step =1)
range() 会返回一个包含所有 k 的列表, 这里 start <= k < end , 从 start 到 end , k 每次递增 step . step 不可以为零,否则将发生错误。如果只给定两个参数,而省略 step, step 就使用默认值 1 。
range()简略语法:range(end) 和 range(start, end)。
核心笔记: 为什么 range() 不是只有一种语法?
range(start=0, end, step =1) # 错误
这个语法不可以使用两个参数调用. 因为 step 要求给定 start . 换句话说, 你不能只传递end 和 step 参数. 因为它们会被解释器误认为是 start 和 end 。
与序列相关的内建函数:sorted(), reversed(), enumerate(), zip()。sorted() 和 zip() )返回一个序列(列表), 而另外两个函数( reversed() 和 enumerate() )返回迭代器(类似序列)。
1 >>>albums = ('Poe', 'Gaudi', 'Freud', 'Poe2') 2 >>> years = (1976, 1987, 1990, 2003) 3 >>> for album in sorted(albums): 4 ... print album, 5 ... 6 Freud Gaudi Poe Poe2 7 >>> 8 >>> for album in reversed(albums): 9 ... print album, 10 ... 11 Poe2 Freud Gaudi Poe 12 >>> 13 >>> for i, album in enumerate(albums): 14 ... print i, album 15 ... 16 0 Poe 17 1 Gaudi 18 2 Freud 19 3 Poe2 20 >>> 21 >>> for album, yr in zip(albums, years): 22 ... print yr, album 23 ... 24 1976 Poe 25 1987 Gaudi 26 1990 Freud 27 2003 Poe2
7.break语句
Python 中的 break 语句可以结束当前循环然后跳转到下条语句,break 语句可以用在 while 和 for 循环中。
8.continue 语句
核心笔记: continue 语句
当遇到continue 语句时, 程序会终止当前循环,并忽略剩余的语句, 然后回到循环的顶端. 在开始下一次迭代前,如果是条件循环, 我们将验证条件表达式.如果是迭代循环,我们将验证是否还有元素可以迭代. 只有在验证成功的情况下, 才会开始下一次迭代。
continue可以被用在 while 和 for 循环里。
1 valid = False 2 count = 3 3 while count > 0: 4 input = input("enter password") 5 # check for valid passwd 6 for eachPasswd in passwdList: 7 if input == eachPasswd: 8 valid = True 9 break 10 if not valid: # (or valid == 0) 11 print "invalid input" 12 count -= 1 13 continue 14 else: 15 break
9.pass 语句
Python 提供了 pass 语句, 它不做任何事情 - 即 NOP , ( No OPeration , 无操作) 我们从汇编语言中借用这个概念。pass 同样也可作为开发中的小技巧, 标记你后来要完成的代码。例如:
def foo_func(): pass 或是 if user_choice == 'do_calc': pass else: pass
10. 再谈 else 语句
在循环中使用时, else子句只在循环完成后执行, 也就是说 break 语句也会跳过 else 块。
1 #!/usr/bin/env python 2 3 def showMaxFactor(num): 4 count = num / 2 5 while count > 1: 6 if num % count == 0: 7 print('largest factor of %d is %d' % (num, count)) 8 break 9 count -= 1 10 else: 11 print num, "is prime" 12 13 for eachNum in range(10, 21): 14 showMaxFactor(eachNum)
11.迭代器和 iter() 函数
迭代器为类序列对象提供了一个类序列的接口。它们是一组数据结构,你可以利用它们的索引从0 开始一直"迭代" 到序列的最后一个条目. 用"计数"的方法迭代序列是很简单的. Python 的迭代无缝地支持序列对象, 而且它还允许程序员迭代非序列类型, 包括用户定义的对象。
为什么要迭代器?
提供了可扩展的迭代器接口.
对列表迭代带来了性能上的增强.
在字典迭代中性能提升.
创建真正的迭代接口, 而不是原来的随机对象访问.
与所有已经存在的用户定义的类以及扩展的模拟序列和映射的对象向后兼容
迭代非序列集合(例如映射和文件)时, 可以创建更简洁可读的代码.
如何迭代?
迭代器就是有一个 next() 方法的对象, 而不是通过索引来计数。 当你或是一个循环机制(例如 for 语句)需要下一个项时, 调用迭代器的 next() 方法就可以获得它。条目全部取出后,会引发一个 StopIteration 异常, 这并不表示错误发生,只是告诉外部调用者, 迭代完成。
reversed() 内建函数将返回一个反序访问的迭代器;enumerate() 内建函数同样也返回迭代器;另外两个新的内建函数, any() 和 all(),如果迭代器中某个/所有条目的值都为布尔真时,则它们返回值为真。
使用迭代器:
1)序列(for 循环会自动调用迭代器的 next() 方法(以及监视StopIteration 异常))
1 >>> myTuple = (123, 'xyz', 45.67) 2 >>> i = iter(myTuple) 3 >>> i.next() 4 123 5 >>> i.next() 6 'xyz' 7 >>> i.next() 8 45.67 9 10 fetch = iter(seq) 11 while True: 12 try: 13 i = fetch.next() 14 except StopIteration: 15 break 16 do_something_to(i)
2)字典
字典和文件是另外两个可迭代的 Python 数据类型. 字典的迭代器会遍历它的键(keys)。语句 for eachKey in myDict.keys() 可以缩写为 for eachKey in myDict。
3)文件
文件对象生成的迭代器会自动调用 readline() 方法。这样, 循环就可以访问文本文件的所有行,也可以使用更简单的 for eachLine in myFile。
记住:在迭代可变对象的时候修改它们并不是个好主意。
一个序列的迭代器只是记录你当前到达第多少个元素, 所以如果你在迭代时改变了元素, 更新会立即反映到你所迭代的条目上.在迭代字典的 key 时, 你绝对不能改变这个字典. 使用字典的 keys() 方法是可以的, 因为keys() 返回一个独立于字典的列表. 而迭代器是与实际对象绑定在一起的, 它将不会继续执行下去。
1 >>> myDict = {'a': 1, 'b': 2, 'c': 3, 'd': 4} 2 >>> for eachKey in myDict: 3 ... print eachKey, myDict[eachKey] 4 ... del myDict[eachKey] 5 ... a 1 6 Traceback (most recent call last): 7 File "", line 1, in ? 8 RuntimeError: dictionary changed size during iteration
如何创建迭代器?
对一个对象调用 iter() 就可以得到它的迭代器. 它的语法如下:
iter(obj)
iter(func, sentinel )
如果你传递一个参数给 iter() , 它会检查你传递的是不是一个序列, 如果是, 那么很简单:根据索引从 0 一直迭代到序列结束. 另一个创建迭代器的方法是使用类。如果是传递两个参数给 iter() , 它会重复地调用 func , 直到迭代器的下个值等于sentinel 。
12.列表解析
列表解析的语法:[expr for iter_var in iterable]
这个语句的核心是 for 循环, 它迭代 iterable 对象的所有条目. 前边的 expr 应用于序列的每个成员, 最后的结果值是该表达式产生的列表. 迭代变量并不需要是表达式的一部分。
列表解析还提供了一个扩展版本的语法:[expr for iter_var in iterable if cond_expr],这个语法在迭代时会过滤/捕获满足条件表达式 cond_expr 的序列成员。
矩阵样例:
>>> [(x+1,y+1) for x in range(3) for y in range(5)] [(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]
磁盘文件样例:
可以通过 for line in data 迭代文件内容,不过, 除了这个, 我们还可以把每行分割( split )为单词, 然后我们可以像这样计算单词个数:
>>> f = open('hhga.txt', 'r')
>>> len([word for line in f for word in line.split()])
91
快速地计算文件大小
import os
>>> os.stat('hhga.txt').st_size
499L
假定文件中至少有一个空白字符, 我们知道文件中有少于 499 个非空字符. 我们可以把每个单词的长度加起来, 得到和:
>>> f.seek(0)
>>> sum([len(word) for line in f for word in line.split()])
408
13.生成器表达式
生成器表达式是列表解析的一个扩展。生成器是特定的函数, 允许你返回一个值, 然后"暂停"代码的执行, 稍后恢复。列表解析的一个不足就是必要生成所有的数据, 用以创建整个列表. 这可能对有大量数据的迭代器有负面效应. 生成器表达式通过结合列表解析和生成器解决了这个问题。
列表解析:
[expr for iter_var in iterable if cond_expr]
生成器表达式:
(expr for iter_var in iterable if cond_expr)
生成器表达式并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来. 生成器表达式使用了"延迟计算"(lazy evaluation), 所以它在使用内存上更有效.
磁盘文件样例:
>>> sum(len(word) for line in data for word in line.split()) ##sum() 函数(它的参数不仅可以是列表,还可以是可迭代对象,比如生成器表达式)
408
交叉配对例子:
1 rows = [1, 2, 3, 17] 2 3 def cols(): # example of simple generator 4 yield 56 5 yield 2 6 yield 1 7 8 x_product_pairs = ((i, j) for i in rows for j in cols()) 9 10 >>> for pair in x_product_pairs: 11 ... print pair 12 ... 13 (1, 56) 14 (1, 2) 15 (1, 1) 16 (2, 56) 17 (2, 2) 18 (2, 1) 19 (3, 56) 20 (3, 2) 21 (3, 1) 22 (17, 56) 23 (17, 2) 24 (17, 1)
重构样例:
读取文件的行的首选方法应该是这样:(readlines() 会读取文件的所有行)
1 f = open('/etc/motd', 'r') 2 longest = 0 3 allLines = f.readlines() 4 f.close() 5 for line in allLines: 6 linelen = len(line.strip()) 7 if linelen > longest: 8 longest = linelen 9 return longest