希望通过博客园持续的更新,分享和记录Python基础知识到高级应用的点点滴滴!
第四波:第5章 条件、循环和其他语句
[总览]
深入介绍条件语句和循环语句,随后会看到列表推导式如何扮演循环和条件语句的角色,最后介绍pass、del和exec语句的用法。
[5.1] print和import的更多信息
首先来看看print和import的几个比较好的特性。
对于跟多应用程序来说,使用logging模块日志比print语句更合适。
[5.1.1] 使用逗号输出
打印多个表达式也是可行的,只要将它们用逗号隔开就好。
print greeting+',',salutation,name
[5.1.2] 把某件事作为另一件事导入
从模块导入函数的时候,可以使用
import somemodule
from somemodule import somefunction
from somemodule import somefunction,anotherfunction,yetanotherfunction
from somemodule import *
from somemodule import somefunction as othername
可以在语句末尾增加一个as子句,或为整个模块提供别名,也可以为函数提供别名。
[5.2] 赋值魔法
赋值语句的一些特殊技巧。
[5.2.1] 序列解包
多个赋值操作可以同时进行:
x,y,z=1,2,3
x,y=y,x
叫做序列解包或可迭代解包---将多个值得序列解开,然后放到变量的序列中。当函数或者方法返回元组(或者其他序列或可迭代对象)时,这个特性尤其有用。
假设需要获取(和删除)字典中任意的键-值对,可以使用popitem方法,这个方法将键-值作为元组返回,那么这个元组就可以直接赋值到两个变量中:
scoundrel={'name':'Robin','girlfriend':'Marion'}
key,value=scoundrel.popitem()
所解包的序列中的元素数量必须和放置在赋值符号=左边的变量数量完全一致。
[5.2.2] 链式赋值
链式赋值是将同一个值赋值给多个变量的捷径。
[5.2.3] 增量赋值
增量赋值,对于*、/、%等标准运算符都适用。增量赋值可以让代码更加紧凑和简练,很多情况下会更易读。
[5.3] 语句块:缩排的乐趣
语句块是在条件为真时执行或者执行多次的一组语句。在代码前放置空格来缩进语句即可创建语句块。使用tab字符也可以缩进语句块,揭示为到下一个tab字符位置的移动,而一个tab字符位置为8个空格。块中的每行都应该缩进同样的量。冒号用来标识语句块的开始,当回退到和已经闭合的块一样的缩进量时,就表示当前块已经结束。
[5.4] 条件和条件语句
介绍让程序选择是否执行语句块的方法。
[5.4.1] 这就是布尔变量的作用
真值,也叫做布尔值。
False、None、0、""、()、[]、{}会被解释器看做假False。其他一切都被解释为真。
布尔值True和False属于布尔类型,bool函数可以用来转换其他值。
[5.4.2] 条件执行和if语句
if语句可以实现条件执行,如果条件判定为真,执行后面的语句块。条件为假,不执行。
[5.4.3] else子句
之所以叫做子句,是因为它不是独立的语句,而只能作为if语句的一部分。
[5.4.4] elif子句
如果需要检查多个条件,就可以使用elif。
[5.4.5] 嵌套代码块
if语句里面可以嵌套使用if语句。
[5.4.6] 更复杂的条件
1、比较运算符
x==y
x<y
x>y
x>=y
x<=y
x!=y
x is y x和y是同一个对象
x is not y x和y是不同的对象
x in y x是y容器的成员
x not in y x不是y容器的成员
Python中比较运算和赋值运算一样是可以连接的,几个运算符可以连在一起使用,比如:0<age<100
2、相等运算符
如果想要知道两个东西是否相等,应该使用相等运算符,即两个等号==。单个相等运算符是赋值运算符,是用来改变值得,而不能用来比较。
3、is:同一性运算符
is运算符是判定同一性而不是相等性的。变量x和y都被绑定到同一个列表上,而变量z被绑定在另外一个具有相同数值和顺序的列表上。它们的值可能相等,但是却不是同一个对象。
总结一下:使用==运算符来判定两个对象是否相等,使用is判定两者是否等同(同一个对象)。
4、in:成员资格运算符
5、字符串和序列比较
字符串可以按照字母顺序排列进行比较。
字符是按照本身的顺序值排列的,一个字母的顺序之可以用ord函数查看,ord函数与chr函数功能相反。如果要忽略大小写字母的区别,可以使用字符串方法upper和lower。
6、布尔运算符
and、or、not。
短路逻辑和条件表达式:
布尔运算符有个有趣的特性:只有在需要求值时才进行求值。举例来说,表达式x and y需要两个变量都为真时才为真,所以如果x为假,表达式就会立刻返回false,而不管y的值。这种行为被称为短路逻辑short-circuit logic或惰性求值。着意味着在布尔运算符之后的所有代码都不会执行。它主要是避免了无用地执行代码。
a if b else c
如果b为真,返回a。否则,返回c。注意这个运算符不用隐去临时变量,就可以直接使用。
[5.4.7] 断言
如果需要确保程序中的某个条件一定为真才能让程序正常工作的话,assert语句就有用了,它可以在程序中置入检查点。
assert 0<age<100,'The age must be realistic!'
[5.5] 循环
[5.5.1] while循环
while not name or name.isspace():
print 'hello ,%s!' % name
[5.5.2] for 循环
可迭代对象是指可以按次序迭代的对象。
Range函数的工作方式类似于分片,它包含下限,但不包含上限。如果希望下限为0,可以只提供上限。
如果能使用for循环,就尽量不用while循环。
xrange函数的循环行为类似于range函数,区别在与range函数一次创建整个序列,而xrange一次只创建一个数。当需要迭代一个巨大的序列时,xrange会更高效。
[5.5.3] 循环遍历字典元素
keys方法来获取键,如果只需要值,可以使用d.values替代d.keys。d.items方法将键-值对作为元组返回。
字典元素的顺序通常是没有意义的。迭代的时候,字典中的键和值都能保证被处理,但是处理顺序不确定。如果顺序很重要的话,可以讲键值保存在单独的列表中,例如在迭代前进行排序。
[5.5.4] 一些迭代工具
在python中迭代序列时,一些函数非常有用。有些函数位于itertools模块中,还有一些python的内建函数也十分有用。
1. 并行迭代
程序可以同时迭代两个序列。
names=['anne','beth','george','damon']
ages=[12,34,32,102]
for i in range(len(names)):
print names[i],'is',age[i],'year old!'
内建的zip函数可以用来进行并行迭代,可以把两个序列“压缩”在一起,然后返回一个元组的列表。
zip(names,ages)
[('anne',12),('beth',34),('george',32),('damon',102)]
for name,age in zip(names,ages):
print name,'is',age,'years old!'
zip函数也可以作用于任意多的序列。很重要的一点是zip可以应付不等长的序列,当最短的序列“用完”的时候就会停止。
zip(range(5),xrange(1000000))
上面的代码中,不推荐用range替换xrange---尽管只需要前5个数字,但range会计算所有的数字,这要花费很长的时间。而使用xrange就没有这个问题,它只计算前5个数字。
2. 编号迭代
要迭代序列中的对象,同时还要获取当前对象的索引。可以使用内建的enumerate函数:
for index,string in enumerate(strings):
if 'xxx' in string:
strings[index]='[censored]'
3. 翻转和排序迭代
reversed和sorted:它们同列表的reverse和sort方法类似,但作用于任何序列或可迭代对象上,不是原地修改对象,而是返回翻转或排序后的版本。
大可在for循环以及join方法中使用,而不会有任何问题。不过却不能直接对它使用索引、分片以及调用list方法。如果希望进行上述处理,那么可以使用list类型转换返回的对象。
[5.5.5] 跳出循环
1. break
2. continue
跳过剩余的循环体,但是不结束循环。
3. while true/break习语
while True:
word=raw_input('Please enter a word:')
if not word:break
print 'The word was '+word
while True的部分实现了一个永远不会自己停止的循环。但是在循环内部的if语句中加入条件可以的,在条件满足时调用break语句。这样一来就可以在循环内部任何地方而不是只在开头终止循环。if/break语句自然地将循环分为两部分:第1部分负责初始化,第2部分则在循环条件为真的情况下使用第1部分内初始化好的数据。
[5.5.6] 循环中的else子句
from math import sqrt
for n in range(99,81,-1)
root=sqet(n)
if root==int(root):
print n
break
else:
print "Didn't find it!"
[5.6] 列表推导式---轻量级循环
列表推导式list comprehension是利用其它列表创建新列表的一种方法。使用普通的圆括号而不是方括号不会得到“元组推导式”。
[5.7] 三人行
另外三个语句:pass、del和exec。
[5.7.1] 什么都没有发生
有的时候,程序什么事情都不用作。pass语句出马了。它可以在代码中做占位符使用。比如程序需要一个if语句,然后进行测试,但是缺少其中一个语句块的代码。因为在python中空代码块是非法的,所以解决方法就是在语句块中加上一个pass语句。
[5.7.2] 使用del删除
一般来说,python会删除那些不再使用的对象,因为使用者不会再通过任何变量或者数据结构引用它们。
当把变量设置为None的时候,字典就“漂”在内存里面了,没有任何名字绑定到它上面。没有办法获取和使用它,所以python解释器直接删除了那个字典,这种行为被称为垃圾收集。
另外一个方法就是使用del语句。它不仅会移除一个对象的引用,也会移除那个名字本身。
当x和y都指向同一个列表:
x=['hello','world']
y=x
y[1]='python'
x
['hello','python']
会有人认为删除x之后,y也就随之消失了,但并非如此:
del x
y
['hello','python']
为什么会这样?x和y都指向同一个列表。但是删除x并不会影响y。原因就是删除的只是名称,而不是列表本身。事实上,在python中是没有办法删除值得(也不需要过多考虑删除值得问题,因为在某个值不再被使用的时候,python解释器会负责内存的回收)。
[5.7.3] 使用exec和eval执行和求值字符串
有时候会需要动态地创造python代码,然后将其作为语句执行或作为表达式计算。但这样做会有很严重的潜在安全漏洞,如果程序将用户提供的一段内容中的一部分字符串作为代码执行,程序可能会失去对代码执行的控制。
1. exec
执行一个字符串的语句是exec。
exec "print 'hello,world!'"
命名空间的概念,或称为作用于scope,是非常重要的知识。现在可以把它想象成保存变量的地方,类似于不可见的字典。所以在程序执行x=1这类赋值语句时,就将键x和值1放在当前的命名空间内,这个命名空间一般来说都是全局命名空间,但这并不是必须的。
2. eval
eval用于“求值”是类似于exec的内建函数。exec语句会执行一系列python语句,而eval会计算python表达式,并且返回结果值。
[5.8] 小结
打印:print语句可用来打印由逗号隔开的多个值。如果语句以逗号结尾,后面的print语句会在同一行内继续打印
导入:可以使用import as语句进行函数的局部重命名。
赋值:通过序列解包和链式赋值功能,多个变量赋值可以一次性赋值,通过增量赋值可以原地改变变量。
块:块是通过缩排使语句成组的一种方法。
条件:条件语句可以根据条件执行或者不执行一个语句块。if/elif/else。这个主题还有一种叫做条件表达式,行如:a if b else c
断言:断言简单来说就是肯定某事为真。如果表达式为假,断言就会让程序崩溃。
循环:可以使用continue语句跳过块中的其他语句然后继续下一次迭代,或者使用break语句跳出循环。
列表推导式:是看起来像循环的表达式,通过列表推导式,可以从旧列表中产生新的列表,对元素应用函数、过滤掉不需要的元素等等。
pass、del、exec、eval:pass语句什么都不做,可作为占位符使用。del语句用来删除变量,或者数据结构的一部分,但是不能用来删除值。exec语句用与执行python程序相同的方式来执行字符串。eval函数对写在字符串中的表达式进行计算并且返回结果。
[5.8.1] 有用函数
chr(n) 当传入序号n时没返回n所代表的包含一个字符的字符串(0≤n<256)
enumerate(seq) 产生用于迭代的(索引,值)对
ord(c) 返回单字符字符串的int值
range([start,],stop[,setp])
xrange([start,],stop[,setp]) 创造xrange对象用于迭代
zip(seq1,seq2,...) 创造用于并行迭代的新序列
reversed(seq) 产生seq中值得反向版本,用于迭代
sorted(seq[,cmp][,key][,reverse]) 返回seq中值排序后的列表