您的位置:首页 > 编程语言 > Python开发

08 条件和循环 - 《Python 核心编程》

2015-08-16 21:12 537 查看
􀁺 if 语句
􀁺 else 语句
􀁺 elif 语句
􀁺 条件表达式
􀁺 while 语句
􀁺 for 语句
􀁺 break 语句
􀁺 continue 语句
􀁺 pass 语句
􀁺 else 语句 (再看)
􀁺 Iterators 迭代器
􀁺 列表解析(List Comprehensions)
􀁺 生成器表达式(Generator Expressions )

8.1 if 语句

Python 中的 if 子句由三部分组成: 关键字本身, 用于判断结果真假的 条件表达式, 以及当表达式为真或者非零时执行的代码块.
if 语句的语法如下:
if expression:
expr_true_suite

多重条件表达式

单个 if 语句可以通过使用布尔操作符 and , or 和 not实现多重判断条件或是否定判断条件.
if not warn and (system_load >= 10):
print "WARNING: losing resources"
warn += 1
单一语句的代码块(不推荐)

如果一个复合语句(例如 if 子句, while 或 for 循环)的代码块仅仅包含一行代码, 那么它可以和前面的语句写在同一行上:
if make_hard_copy: send_data_to_printer()

这样会使得代码更难阅读, 我们推 荐将这行代码移到下一行并合理地缩进.
如果你需要添加新的代码, 你还是得把 它移到下一行.
8.2 else 语句
和其他语言一样, Python 提供了与 if 语句搭配使用的 else 语句.

if expression:
expr_true_suite
else:
expr_false_suite

避免“悬挂 else”

/* dangling-else in C */
if (balance > 0.00)
if (((balance - amt) > min_bal) && (atm_cashout() == 1))
printf("Here's your cash; please take all bills.\n");
else // ?? if
printf("Your balance is zero or negative.\n");

8.3 elif (即 else-if )语句

if 语句后最多只能有一个 else语句, 但可以有任意数量的 elif 语句.
用字典, 序列和成员关系操作符来简化
用映射对象(比如字典)的一个最大好处就是它的搜索操作比类似 if-elif-else语句或是 for 循环这样的序列查询要快很多.
在 Python 中, 大量的 if-elif 语句并不难阅读:
if user.cmd == 'create':
action = "create item"
elif user.cmd == 'delete':
action = 'delete item'
elif user.cmd == 'update':
action = 'update item'
else:
action = 'invalid choice... try again!'
我们还可以用序列和成员关系操作符来简化它:
if user.cmd in ('create', 'delete', 'update'):
action = '%s item' % user.cmd
else:
action = 'invalid choice... try again!'
我们可以用 Python 字典给出更加优雅的解决方案
使用映射对象(比如字典)的一个最大好处就是它的搜索操作比类似 if-elif-else 语句或是 for 循环这样的序列查询要快很多.
msgs = {'create': 'create item', 'delete': 'delete item', 'update': 'update item'}
default = 'invalid choice... try again!'
action = msgs.get(user.cmd, default)
8.4 条件表达式(即"三元操作符")
如果你来自 C/C++ 或者是 Java 世界, 那么你很难忽略的一个事实就是 Python 在很长的一 段时间里没有条件表达式(C ? X : Y), 或称三元运算符.
C 是条件表达式; X 是 C 为 True 时的结果, Y 是 C 为 False 时的结果. 贵铎·范·罗萨姆一直拒绝加入这样的功能, 因为他认为应该保持代码简单, 让程序员不轻易出错.
不过在十年多后, 他放弃了, 主要是因为人们试着用and 和 or 来模拟它, 但大多都是错误的.根据 FAQ , 正确的方法(并不唯一)是 (C and [X] or [Y])[0] .
最后 Python 2.5 集成的语法确定为: X if C else Y .
8.5 while 语句
Python 的 while 是一个条件循环语句. 与 if 声明相比, 如果 if 后的条件为真, 就会执行一次相应的代码块. 而 while 中的代码块会一直循环执行, 直到循环条件不再为真.
一般语法

while expression:
suite_to_repeat

while 循环的 suite_to_repeat 子句会一直循环执行, 直到 expression 值为布尔假. 这种类型的循环机制常常用在计数循环中.

计数循环
count = 0
while (count < 9):
print 'the index is:', count
count += 1

无限循环

你必须小心地使用 while 循环, 因为有可能 condition 永远不会为布尔假. 这样一来循环 就永远不会结束.
这些"无限"的循环不一定是坏事, 许多通讯服务器的客户端/服务器系统就是通过它来工作的.
这取决于循环是否需要一直执行下去, 如果不是, 那么这个循环是否会结束; 也就是说, 条件表达式会不会计算后得到布尔假?
while True:
handle, indata = wait_for_client_connect()
outdata = process_request(indata)
ack_result_to_client(handle, outdata)
8.6 for 语句

Python 提供的另一个循环机制就是 for 语句, 可以遍历序列成员, 可以用在列表解析和生成器表达式中, 它会自动地调用迭代器的 next() 方法, 捕获 StopIteration 异常并结束循环.
和传统语言(例如 C/C++ , Fortran, 或者 Java )中的 for 语句不同, Python 的 for 更像是 shell 或是脚本语言中的 foreach 循环.
一般语法
for 循环会访问一个可迭代对象(例如序列或是迭代器)中的所有元素, 并在所有条目都处理过 后结束循环.
for iter_var in iterable:
suite_to_repeat
每次循环, iter_var 迭代变量被设置为可迭代对象(序列, 迭代器, 或者是其他支持迭代的对 象)的当前元素, 提供给 suite_to_repeat 语句块使用.
用于序列类型
通过序列项迭代
>>> nameList = ['Walter', "Nicole", 'Steven', 'Henry']
>>> for eachName in nameList:
... print eachName, "Lim"
...
Walter Lim
Nicole Lim
Steven Lim
Henry Lim
在上面的例子中, 我们迭代一个列表. 每次迭代, eachName 变量都被设置为列表中特定某个元 素, 然后我们在代码块中打印出这个变量.
通过序列索引迭代
>>> nameList = ['Cathy', "Terry", 'Joe', 'Heather', 'Lucy']
>>> for nameIndex in range(len(nameList)):
... print "Liu,", nameList[nameIndex]
...
Liu, Cathy
Liu, Terry
Liu, Joe
Liu, Heather
Liu, Lucy
这里我们使用了内建的 len() 函数获得序列长度, 使用 range() 我们可以得到用来迭代 nameList 的索引数列表, 使用切片/下标操作符( [ ] ), 就可以访问对应的序列对象.
使用项和索引迭代
两全其美的办法是使用内建的 enumerate() 函数, 它是 Python 2.3 的新增内容.
>>> nameList = ['Donn', 'Shirley', 'Ben']
>>> for i, eachLee in enumerate(nameList):
... print "%d %s Lee" % (i+1, eachLee)
...
1 Donn Lee
2 Shirley Lee
3 Ben Lee
用于迭代器类型

用 for 循环访问迭代器和访问序列的方法差不多.
唯一的区别就是 for 语句会为你做一些额 外的事情.
迭代器并不代表循环条目的集合.
迭代器对象有一个 next() 方法, 调用后返回下一个条目.
所有条目迭代完后, 迭代器引发一 个 StopIteration 异常告诉程序循环结束.
for 语句在内部调用 next() 并捕获异常.
使用迭代器做 for 循环的代码与使用序列条目几乎完全相同.
事实上在大多情况下, 你无法 分辨出你迭代的是一个序列还是迭代器.
因此,这就是为什么我们在说要遍历一个迭代器时,实际上可能我们指的是要遍历一个序列,迭代器,或是一个支持迭代的对象(它有next()方法)。
range() 内建函数
Python 的 for 循环的是一种迭代的循环机制. 内建函数 range() 可以把类似 foreach 的 for 循环变成你更加熟悉的语句. 例如从 0 到 10 计数, 或者从 10 到 100 一次递增 5 .
range() 的完整语法
range(start, end, step =1)
range() 简略语法
range(end)
核心笔记: 为什么 range() 不是只有一种语法?
你已经知道了 range() 的所有语法, 有些人可能会问一个挑剔的问题, 为什么不把这两种语 法合并成一个下面这样的语法?
range(start=0, end, step =1) # 错误
这个语法不可以使用两个参数调用. 因为 step 要求给定 start . 换句话说, 你不能只传递 end 和 step 参数. 因为它们会被解释器误认为是 start 和 end .
xrange() 内建函数
xrange() 类似 range() , 不过当你有一个很大的范围列表时, xrange() 可能更为适合.
xrange() 不会在内存里创建列表的完整拷贝. 它只被用在 for 循环中, 在 for 循环外使用它没有意义.
xrange() 性能远高出 range(), 因为它不生成整个列表.
与序列相关的内建函数
sorted(), reversed(), enumerate(), zip()
sorted() 和 zip() )返回一个序列(列表).
reversed() 和 enumerate() 返回迭代器(类似序列)

>>> albums = ('Poe', 'Gaudi', 'Freud', 'Poe2')
>>> years = (1976, 1987, 1990, 2003)
>>> for album in sorted(albums):
... print album,
...
Freud Gaudi Poe Poe2
>>>
>>> for album in reversed(albums):
... print album,
...
Poe2 Freud Gaudi Poe
>>>
>>> for i, album in enumerate(albums):
... print i, album
...
0 Poe
1 Gaudi
2 Freud
3 Poe2
>>>
>>> for album, yr in zip(albums, years):
... print yr, album
...
1976 Poe
1987 Gaudi
1990 Freud
2003 Poe2
8.7 break 语句 (括用于放弃循环的 break 语句)

Python 中的 break 语句可以结束当前循环然后跳转到下条语句, 类似 C 中的传统 break .
常用在当某个外部条件被触发(一般通过 if 语句检查), 需要立即从循环中退出时.
break 语句可以用在 while 和 for 循环中.

8.8 continue 语句 (立即开始下一次迭代的 continue 语句)

continue 语句可以被用在 while 和 for 循环里.
while 循环是条件性的, 而 for 循环是迭代的, 所以 continue 在开始下一次循环前要满足一些先决条件, 否则循环会正常结束.
核心笔记: continue 语句
不管是Python, C, Java 还是其它任何支持continue 语句的结构化语言中, 一些初学者有这样
的一个误解:continue 语句"立即启动循环的下一次迭代". 实际上, 当遇到continue 语句时, 程
序会终止当前循环,并忽略剩余的语句, 然后回到循环的顶端. 在开始下一次迭代前,如果是条件循
环, 我们将验证条件表达式.如果是迭代循环,我们将验证是否还有元素可以迭代. 只有在验证成功
的情况下, 我们才会开始下一次迭代.

8.9 pass 语句

Python 没有使用传统的大括号来标记代码块.
有时,有些地方在语法上要求要有代码, 而Python 中没有对应的空大括号或是分号( ; )来表示C 语言中的 "不做任何事" .
如果你在需要子语句块的地方不写任何语句, 解释器会提示你语法错误.
因此, Python 提供了 pass 语句, 它不做任何事情 - 即 NOP , ( No OPeration , 无操作) 我们从汇编语言中借用这个概念.
pass 同样也可作为开发中的小技巧, 标记你后来要完成的代码, 例如这样:
def foo_func():
pass
或是
if user_choice == 'do_calc':
pass else:
pass

这样的代码结构在开发和调试时很有用, 因为编写代码的时候你可能要先把结构定下来,但你不希望它干扰其他已经完成的代码.
在不需要它做任何事情地方, 放一个pass 将是一个很好的主意.
另外它在异常处理中也被经常用到, 比如你跟踪到了一个非致命的错误, 不想采取任何措施(你只是想记录一下事件或是在内部进行处理).
8.10 再谈 else 语句

for 循环和 while 循环可以有 else 用于循环后处理(post-processing), 只要循环是正常结束的(不是通过 break ), else 子句就会执行.



pass 在任何需要语句块(一个或多个语句)的地方都可以使用(例如 elif , else , clasa ,def , try , except , finally ).

8.11 迭代器和 iter() 函数
什么是迭代器?
迭代器是在版本 2.2 被加入 Python 的, 它为类序列对象提供了一个类序列的接口.
Python 的迭代无缝地支持 序列对象, 而且它还允许程序员迭代非序列类型, 包括用户定义的对象.
为什么要迭代器?
援引 PEP (234) 中对迭代器的定义:
􀁺 提供了可扩展的迭代器接口.
􀁺 对列表迭代带来了性能上的增强.
􀁺 在字典迭代中性能提升.
􀁺 创建真正的迭代接口, 而不是原来的随机对象访问.
􀁺 与所有已经存在的用户定义的类以及扩展的模拟序列和映射的对象向后兼容
􀁺 迭代非序列集合(例如映射和文件)时, 可以创建更简洁可读的代码.
如何迭代?
迭代器就是有一个 next() 方法的对象, 而不是通过索引来计数.
当你或是一个循 环机制(例如 for 语句)需要下一个项时, 调用迭代器的 next() 方法就可以获得它.
条目全部取出后, 会引发一个 StopIteration 异常, 这并不表示错误发生, 只是告诉外部调用者, 迭代完成.
迭代器也有一些限制.
例如你不能向后移动, 不能回到开始, 也不能复制一个迭代器.
如果你要再次(或者是同时)迭代同个对象, 你只能去创建另一个迭代器对象. 不过, 这并不糟糕, 因为还有其他的工具来帮助你使用迭代器.
reversed() 内建函数
reversed() 内建函数将返回一个反序访问的迭代器.
enumerate() 内建函数
enumerate() 内建函数同样也返回迭代器.
any() 和 all() 内建函数
两个新的内建函数, any() 和 all() , 在 Python 2.5 中新增, 如果迭代器中某个/所有条目 的值都为布尔真时,则它们返回值为真.
使用迭代器
序列
>>> t = ('hello','world')
>>> i = iter(t)
>>> i.next()
'hello'
>>> i.next()
'world'
>>> i.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> for i in t: #for 循环会自动调用迭代器的 next() 方法(以及监视 StopIteration 异常).
... i
...
'hello'
'world'
>>> fetch = iter(t)
>>> while True:
... try:
... fetch.next()
... except StopIteration:
... break
...
'hello'
'world'
>>>
字典
字典和文件是另外两个可迭代的 Python 数据类型.
字典的迭代器会遍历它的键(keys).
语句 for eachKey in myDict.keys() 可以缩写为 for eachKey in myDict.
Python 还引进了三个新的内建字典方法来定义迭代:
myDict.iterkeys() (通过 keys 迭 代),
myDict.itervalues() (通过 values 迭代),
以及 myDicit.iteritems() (通过 key/value 对来迭代).
文件
文件对象生成的迭代器会自动调用 readline() 方法. 这样, 循环就可以访问文本文件的所有 行.
程序员可以使用更简单的 for eachLine in myFile 替换 for eachLine in myFile.readlines() :
>>> myFile = open('config-win.txt')
>>> for eachLine in myFile:
... print eachLine, # comma suppresses extra \n
...
[EditorWindow]
font-name: courier new
font-size: 10
>>> myFile.close()
可变对象和迭代器
记住,在迭代可变对象的时候修改它们并不是个好主意.
一个流行的例子就是循环列表的时候删除满足(或不满足)特定条件的项:
for eachURL in allURLs:
if not eachURL.startswith('http://'):
allURLs.remove(eachURL) # YIKES!!
除列表外的其他序列都是不可变的, 所以危险就发生在这里.
一个序列的迭代器只是记录你 当前到达第多少个元素, 所以如果你在迭代时改变了元素, 更新会立即反映到你所迭代的条目上.
在迭代字典的 key 时, 你绝对不能改变这个字典.
使用字典的 keys() 方法是可以的, 因为 keys() 返回一个独立于字典的列表. 而迭代器是与实际对象绑定在一起的, 它将不会继续执行下去:
>>> myDict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> for eachKey in myDict:
... print eachKey, myDict[eachKey]
... del myDict[eachKey]
... a 1
Traceback (most recent call last):
File "", line 1, in ?
RuntimeError: dictionary changed size during iteration
这样可以避免有缺陷的代码.
如何创建迭代器
对一个对象调用 iter() 就可以得到它的迭代器. 它的语法如下:
iter(obj)
iter(func, sentinel )
如果你传递一个参数给 iter() , 它会检查你传递的是不是一个序列, 如果是, 那么很简单: 根据索引从 0 一直迭代到序列结束.
另一个创建迭代器的方法是使用类, 一个实现了 __iter__() 和 next() 方法的类可以作为迭代器使用.
如果是传递两个参数给 iter() , 它会重复地调用 func , 直到迭代器的下个值等于 sentinel .
8.12 列表解析
列表解析( List comprehensions, 或缩略为 list comps ) 来自函数式编程语言 Haskell . 它 是一个非常有用, 简单, 而且灵活的工具, 可以用来动态地创建列表.
Python 支持的函数式编程特性, 例如 lambda , map() , 以及 filter() 等, 但通过列表解析 , 它们可以被简化 为一个列表解析式子.
map()
map() 对所有的列表成员应用一个操作,
filter()
filter() 基于一个条件表达式过滤列表成员,
lambda
lambda 允许你快速地创建只有一行的函数对象.
列表解析的语法:
标准语法
[expr for iter_var in iterable]
这个语句的核心是 for 循环, 它迭代 iterable 对象的所有条目.
前边的 expr 应用于序列 的每个成员, 最后的结果值是该表达式产生的列表.
迭代变量并不需要是表达式的一部分.
列表解析的表达式可以取代内建的 map() 函数以及 lambda , 而且效率更高.
>>> map(lambda x: x**2, range(6))
[0, 1, 4, 9, 16, 25]
>>> [x**2 for x in range(6)]
[0, 1, 4, 9, 16, 25]
>>>
扩展语法
结合if 语句,列表解析还提供了一个扩展版本的语法:
[expr for iter_var in iterable if cond_expr]
这个语法在迭代时会过滤/捕获满足条件表达式 cond_expr 的序列成员.
>>> seq = [11, 10, 9, 9, 10, 10, 9, 8, 23, 9, 7, 18, 12, 11, 12]
>>> filter(lambda x: x % 2, seq)
[11, 9, 9, 9, 23, 9, 7, 11]
>>> [x for x in seq if x % 2]
[11, 9, 9, 9, 23, 9, 7, 11]
>>>
矩阵样例
>>> [(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)]
>>>
8.13 生成器表达式
生成器表达式是列表解析的一个扩展.
生成器是特定的函数, 允许你返回一个值, 然后"暂停"代码的执行, 稍后恢复.
列表解析的一个不足就是必要生成所有的数据, 用以创建整个列表.
这可能对有大量数据的迭 代器有负面效应.
生成器表达式通过结合列表解析和生成器解决了这个问题.
生成器表达式与列表解析非常相似,而且它们的基本语法基本相同;
生成器表达式并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这 个条目“产生”(yield)出来.
生成器表达式使用了"延迟计算"(lazy evaluation), 所以它在使用内存上更有效.
列表解析:
[expr for iter_var in iterable if cond_expr] #List ?
生成器表达式:
(expr for iter_var in iterable if cond_expr) #Tuple ?
生成器并不会让列表解析废弃, 它只是一个内存使用更友好的结构, 基于此, 有很多使用生 成器地方.
8.14 R 相关模块
itertools 模块
迭代器和生成器
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: