详解Python中的循环语句的用法
一、简介
Python的条件和循环语句,决定了程序的控制流程,体现结构的多样性。须重要理解,if、while、for以及与它们相搭配的else、elif、break、continue和pass语句。
二、详解
1、if语句
Python中的if子句由三部分组成:关键字本身、用于判断结果真假的条件表达式以及当表达式为真或者非零时执行的代码块。if语句的语法如下:
ifexpression: expr_true_suite
if语句的expr_true_suite代码块只有在条件表达式的结果的布尔值为真时才执行,否则将继续执行紧跟在该代码块后面的语句。
(1)多重条件表达式
单个if语句可以通过使用布尔操作符and、or和not,实现多重判断条件或是否定判断条件。
(2)单一语句的代码块
如果一个复合语句(例如if子句、while或for循环)的代码块仅仅包含一行代码,那么它可以和前面的语句写在同一行上。如ifmake_hard_copy:send_data_to_printer(),这样的单行语句是合法的,尽管它可能方便,但这样会使得代码更难阅读,所以推荐将这行代码移到下一行并合理地缩进。另外一个原因就是如果你需要添加新的代码,你还是得把它移到下一行。
2、else语句
Python提供了与if语句搭配使用的else语句,如果if语句的条件表达式的结果布尔值为假,那么程序将执行else语句后的代码。其语法如下:
ifexpression: expr_true_suite else: expr_false_suite
在C语言中,不会在条件语句范围外发现else语句,但Python不同,可以在while和for循环中使用else语句,在循环中使用时,else子句只在循环完成后执行,也就是说break语句也会跳过else块。
例:显示出10到20中的数字的最大约数
在CODE上查看代码片派生到我的代码片
#!/usr/bin/envpython defshowMaxFactor(num): count=num/2 whilecount>1: if(num%count==0): print'largestfactorof%dis%d'%(num,count) break count=count-1 else: printeachNum,'isprime' foreachNuminrange(10,21): showMaxFactor(eachNum)
在CODE上查看代码片派生到我的代码片
largestfactorof10is5 11isprime largestfactorof12is6 13isprime largestfactorof14is7 largestfactorof15is5 largestfactorof16is8 17isprime largestfactorof18is9 19isprime largestfactorof20is10
3、elif(即else-if)语句
elif是Python的else-if语句,它检查多个表达式是否为真,并在为真时执行特定代码块中的代码。和else一样,elif声明是可选的,然而不同的是if语句后最多只能有一个else语句,但可以有任意数量的elif语句。
ifexpression1: expr1_true_suite elifexpression2: expr2_true_suite ... elifexpressionN: exprN_true_suite else: none_of_the_above_suite
在将来的某天,Python可能会支持switch/case语句,但是完全可以用其他的Python结构来模拟它。在Python中,大量的if-elif语句并不难阅读。
在CODE上查看代码片派生到我的代码片
ifuser.cmd=='create': action="createitem" elifuser.cmd=='delete': action='deleteitem' elifuser.cmd=='update': action='updateitem' else: action='invalidchoice...tryagain!'
上面的语句还可以用序列和成员关系操作符来简化它:
在CODE上查看代码片派生到我的代码片
ifuser.cmdin('create','delete','update'): action='%sitem'%user.cmd else: action='invalidchoice...tryagain!'
还可以用Python字典给出更加优雅的解决方案,使用映射对象(比如字典)的一个最大好处就是它的搜索操作比类似语句或是for循环这样的序列查询要快很多。
在CODE上查看代码片派生到我的代码片
msgs={'create':'createitem', 'delete':'deleteitem', 'update':'updateitem' } default='invalidchoice...tryagain!' action=msgs.get(user.cmd,default)
4、条件表达式(即"三元操作符")
三元运算符语法为:XifCelseY,只需要一行完成条件判断和赋值操作:
在CODE上查看代码片派生到我的代码片
>>>x,y=4,3 >>>smaller=xifx<yelsey >>>smaller 3
5、while语句
while是一个条件循环语句,与if声明相比,如果if后的条件为真,就会执行一次相应的代码块。而while中的代码块会一直循环执行,直到循环条件不再为真。
(1)一般语法
while循环的语法如下:
whileexpression: suite_to_repeat
while循环的suite_to_repeat子句会一直循环执行,直到expression值为布尔假。
(2)计数循环
count=0 while(count<9): print'theindexis:',count count+=1
代码块里包含了print和自增语句,它们被重复执行,直到count不再小于9。索引count在每次迭代时被打印出来然后自增1。
(3)无限循环
whileTrue: handle,indata=wait_for_client_connect() outdata=process_request(indata) ack_result_to_client(handle,outdata)
“无限”循环永远不会结束,但它不一定是坏事,许多通讯服务器的客户端/服务器系统就是通过它来工作的。
6、for语句
Python提供了的另一个循环机制就是for语句,它是Python中最强大的循环结构。它可以遍历序列成员,可以用在列表解析和生成器表达式中,它会自动地调用迭代器的next()方法,捕获StopIteration异常并结束循环(所有这一切都是在内部发生的)。Python的for更像是shell或是脚本语言中的foreach循环。
(1)一般语法
for循环会访问一个可迭代对象(例如序列或是迭代器)中的所有元素,,并在所有条目都处理过后结束循环。它的语法如下:
foriter_variniterable:
suite_to_repeat
每次循环,iter_var迭代变量被设置为可迭代对象(序列、迭代器或者是其他支持迭代的对象)的当前元素,提供给suite_to_repeat语句块使用。
(2)用于序列类型
for循环可以迭代不同的序列对象,像字符串、列表、以及元组。
迭代序列有三种基本方法:
通过序列项迭代
在CODE上查看代码片派生到我的代码片
>>>nameList=['Walter',"Nicole",'Steven','Henry'] >>>foreachNameinnameList: ...printeachName,"Lim" ... WalterLim NicoleLim StevenLim HenryLim
迭代一个列表.。每次迭代,eacgName变量都被设置为列表中特定某个元素。
通过序列索引迭代
在CODE上查看代码片派生到我的代码片
>>>nameList=['Cathy',"Terry",'Joe','Heather','Lucy'] >>>fornameIndexinrange(len(nameList)): ...print"Liu,",nameList[nameIndex] ... Liu,Cathy Liu,Terry Liu,Joe Liu,Heather Liu,Lucy
没有迭代元素,而是通过列表的索引迭代。但通过直接迭代序列要比索引迭代快。
使用项和索引迭代
在CODE上查看代码片派生到我的代码片
>>>nameList=['Donn','Shirley','Ben','Janice','David','Yen','Wendy'] >>>fori,eachLeeinenumerate(nameList): ...print"%d%sLee"%(i+1,eachLee) ... 1DonnLee 2ShirleyLee 3BenLee 4JaniceLee 5DavidLee 6YenLee 7WendyLee
(3)用于迭代器类型
用for循环访问迭代器和访问序列的方法差不多,迭代器并不代表循环条目的集合,迭代器对象有一个next()方法,调用后返回下一个条目。所有条目迭代完后,迭代器引发一个StopIteration异常告诉程序循环结束,for语句在内部调用next()并捕获异常。
使用迭代器做for循环的代码与使用序列条目几乎完全相同。事实上在大多情况下,无法分辨出迭代的是一个序列还是迭代器,因此遍历一个迭代器时,实际上可能指的是要遍历一个序列、迭代器或是一个支持迭代的对象(它有next()方法)。
(4)range()内建函数
内建函数range()可以把类似foreach的for循环变成你更加熟悉的语句。
Python提供了两种不同的方法来调用range(),完整语法要求提供两个或三个整数参数:range(start,end,step=1),range()会返回一个包含所有k的列表,其中start<=k<end,从start到end,k每次递增ste,step不可以为零,否则将发生错误。
在CODE上查看代码片派生到我的代码片
>>>range(3,7) [3,4,5,6] >>>foreachValinrange(2,19,3): ...print"valueis:",eachVal ... valueis:2 valueis:5 valueis:8 valueis:11 valueis:14 valueis:17
range()还有两种简略的语法格式:range(end)和range(start,end)。start默认为0,step默认为1。
(5)xrange()内建函数
xrange()类似range(),不过当有一个很大的范围列表时,xrange()可能更为适合,因为它不会在内存里创建列表的完整拷贝。它只被用在for循环中,在for循环外使用它没有意义。它的性能远高出range(),因为它不生成整个列表。在Python的将来版本中,range()可能会像xrange()一样,返回一个可迭代对象(不是列表也不是一个迭代器)。
(6)与序列相关的内建函数
序列相关函数:sorted()、reversed()、enumerate()和zip(),称为“序列相关”是因为其中两个函数(sorted()和zip())返回一个序列(列表),而另外两个函数(reversed()和enumerate())返回迭代器(类似序列)。
7、break和continue语句
Python中的break语句可以结束当前循环然后跳转到下条语句,类似C中的break。常用在当某个外部条件被触发(一般通过if语句检查),需要立即从循环中退出时.。break语句可以用在while和for循环中。
Python里的continue语句和其他高级语言中的传统continue并没有什么不同,它可以被用在while和for循环里。while循环是条件
性的,而for循环是迭代的,所以continue在开始下一次循环前要满足一些先决条件,否则循环会正常结束。
程序中当遇到continue语句时,程序会终止当前循环,并忽略剩余的语句,然后回到循环的顶端。在开始下一次迭代前,如果是条件循环,我们将验证条件表达式。如果是迭代循环,将验证是否还有元素可以迭代。只有在验证成功的情况下,才会开始下一次迭代。
在CODE上查看代码片派生到我的代码片
#!/usr/bin/envpython valid=False count=3 passwdList=('abc',) whilecount>0andvalid==False: input=raw_input("enterpassword:").strip() #checkforvalidpasswd foreachPasswdinpasswdList: ifinput==eachPasswd: valid=True break ifnotvalid:#(orvalid==0) print"invalidinput" count-=1 continue else: break
结合使用了while、for、if、break以及continue,来验证用户输入。用户有三次机会来输入正确的密码,阻止用户猜测密码。
8、pass语句
Python中没有对应的空大括号或是分号(;)来表示如C语言中的“不做任何事”,如果需要子语句块的地方不写任何语句,解释器会提示语法错误。因此,Python提供了pass语句,它不做任何事情,即NOP(NoOPeration),pass同样也可作为开发中的小技巧,标记以后将要完成的代码。
deffoo_func():
pass
这样的代码结构在开发和调试时很有用,因为编写代码的时候可能要先把结构定下来,但又不希望它干扰其他已经完成的代码,在不需要它做任何事情地方放一个pass,将是一个很好的主意。另外它在异常处理中也被经常用到,比如你跟踪到了一个非致命的错误而不想采取任何措施。
9、迭代器和iter()函数
(1)什么是迭代器
迭代器为类序列对象提供了一个类序列的接口,可以利用它们的索引从0开始一直"迭代"到序列的最后一个条目,用"计数"的方法迭代序列是很简单的。Python的迭代无缝地支持序列对象,而且它还允许程序员迭代非序列类型,包括用户定义的对象。
迭代器用起来很灵巧,可以迭代不是序列但表现出序列行为的对象,例如字典的key、一个文件的行等等。当使用循环迭代一个对象条目时,不必去关注它是迭代器还是序列。
(2)为什么要迭代器
迭代器的定义:提供了可扩展的迭代器接口、对列表迭代带来了性能上的增强、在字典迭代中性能提升、创建真正的迭代接口,而不是原来的随机对象访问、与所有已经存在的用户定义的类以及扩展的模拟序列和映射的对象向后兼容、迭代非序列集合(例如映射和文件)时,可以创建更简洁可读的代码。
(3)如何迭代
迭代器有一个next()方法的对象,而不是通过索引来计数。当一个循环机制(例如for语句)需要下一个项时,调用迭代器的next()方法就可以获得它。条目全部取出后,会引发一个StopIteration异常,这并不表示错误发生,只是告诉外部调用者迭代完成。
不过,迭代器也有一些限制。例如不能向后移动,不能回到开始,也不能复制一个迭代器。如果要再次(或者是同时)迭代同个对象,你只能去创建另一个迭代器对象。不过,还有其他的工具来帮助你使用迭代器。
reversed()内建函数将返回一个反序访问的迭代器。enumerate()内建函数同样也返回迭代器。另外两个新的内建函数:any()和all(),如果迭代器中某个/所有条目的值都为布尔真时,则它们返回值为真。
(4)使用迭代器
序列
在CODE上查看代码片派生到我的代码片
>>>myTuple=(123,'xyz',45.67) >>>i=iter(myTuple) >>>i.next() 123 >>>i.next() 'xyz' >>>i.next() 45.670000000000002 >>>i.next() Traceback(mostrecentcalllast): File"<stdin>",line1,in<module> StopIteration
在for循环中foriinseq:do_something_to(i),它会自动调用迭代器的next()方法,并且监视StopIteration异常。
字典
字典和文件是另外两个可迭代的Python数据类型。字典的迭代器会遍历它的键(keys),语句foreachKeyinmyDict.keys()可以缩写为foreachKeyinmyDict。
Python还引进了三个新的内建字典方法来定义迭代:myDict.iterkeys()(通过keys迭代),myDict.itervalues()(通过values迭代)以及myDicit.iteritems()(通过key/value对来迭代)。注意:in操作符也可以用于检查字典的key是否存在,布尔表达式myDict.has_key(anyKey)可以被简写为anyKeyinmyDict。
文件
文件对象生成的迭代器会自动调用readline()方法。这样,循环就可以访问文本文件的所有行。可以使用更简单的foreachLineinmyFile替换foreachLineinmyFile.readlines()。
(5)可变对象和迭代器
在迭代可变对象的时候修改它们并不是个好主意,这在迭代器出现之前就是一个问题。一个序列的迭代器只是记录当前到达第几个元素,所以若在迭代时改变了元素,更新会立即反映到你所迭代的条目上。在迭代字典的key时,绝对不能改变这个字典。使用字典的keys()方法是可以的,因为keys()返回一个独立于字典的列表,而迭代器是与实际对象绑定在一起的,它将不会继续执行下去。
(6)如何创建迭代器
对一个对象调用iter()就可以得到它的迭代器,它的语法如下:iter(obj)或iter(func,sentinel)。如果传递一个参数给iter(),它会检查你传递的是不是一个序列,如果是则会根据索引从0一直迭代到序列结束。另一个创建迭代器的方法是使用类,一个实现__iter__()和next()方法的类可以作为迭代器使用。如果是传递两个参数给iter(),它会重复地调用func,直到迭代器的下个值等于sentinel。
10、列表解析
列表解析(Listcomprehensions或缩略为listcomps)来自函数式编程语言Haskell。它是一个非常有用、简单、而且灵活的工具,可以用来动态地创建列表。
Python支持的函数式编程特性,例如lambda、map()以及filter()等,通过列表解析它们可以被简化为一个列表解析式子。map()对所有的列表成员应用一个操作,filter()基于一个条件表达式过滤列表成员,lambda()允许快速地创建只有一行的函数对象。
列表解析的语法:[exprforiter_variniterable],它迭代iterable对象的所有条目。其中的expr应用于序列的每个成员,最后的结果值是该表达式产生的列表,迭代变量并不需要是表达式的一部分。
在CODE上查看代码片派生到我的代码片
>>>[x**2forxinrange(6)] [0,1,4,9,16,25]
列表解析的表达式可以取代内建的map()函数以及lambda,而且效率更高。结合if语句,列表解析还提供了一个扩展版本的语法:[exprforiter_variniterableifcond_expr],它在迭代时会过滤/捕获满足条件表达式cond_expr的序列成员。
挑选出序列中的奇数:
在CODE上查看代码片派生到我的代码片
>>>seq=[11,10,9,9,10,10,9,8,23,9,7,18,12,11,12] >>>filter(lambdax:x%2,seq) [11,9,9,9,23,9,7,11] >>>[xforxinseqifx%2] [11,9,9,9,23,9,7,11]
即使不用filter()和lambda,可以使用列表解析来完成操作,获得想要的数字。
矩阵样例:迭代一个有三行五列的矩阵,[(x+1,y+1)forxinrange(3)foryinrange(5)]。
磁盘文件样例:若有一个数据文件text.txt,需要计算出所有非空白字符的数目,可以把每行分割(split)为单词,,然后计算单词个数:>>>f=open('hhga.txt','r');len([wordforlineinfforwordinline.split()])。快速地计算文件大小:>>>importos;os.stat('text.txt').st_size。把每个单词的长度加起来:>>>f.seek(0);sum([len(word)forlineinfforwordinline.split()])。
11、生成器表达式
生成器表达式是列表解析的一个扩展,只用一行代码就可以创建包含特定内容的列表。另一个重要特性是生成器,生成器是特定的函数,允许返回一个值,然后"暂停"代码的执行,稍后恢复。
列表解析的一个不足就是必要生成所有的数据,用以创建整个列表。这可能对有大量数据的迭代器有负面效应,生成器表达式通过结合列表解析和生成器解决了这个问题。
生成器表达式与列表解析非常相似,而且它们的基本语法基本相同。不过它并不真正创建数字列表而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来。生成器表达式使用了"延迟计算"(lazyevaluation),所以它在使用内存上更有效。生成器并不会让列表解析废弃,它只是一个内存使用更友好的结构,基于此,有很多使用生成器地方。
列表解析语法:
[exprforiter_variniterableifcond_expr]
生成器表达式语法:
(exprforiter_variniterableifcond_expr)
磁盘文件样例:上述计算文本文件中非空白字符总和,如果这个文件的大小变得很大,那么这行代码的内存性能会很低,因为要创建一个很长的列表用于存放单词的长度。为了避免创建庞大的列表,使用生成器表达式来完成求和操作,优化后的代码:>>>sum(len(word)forlineindataforwordinline.split()),是把方括号删除,少了两字节,而且更节省内存。
交叉配对例子:成器表达式就好像是懒惰的列表解析(这反而成了它主要的优势),它还可以用来处理其他列表或生成器,如:x_product_pairs=((i,j)foriinrowsforjincols())。
重构样例,寻找文件最长的行的例子:
以前的方法:
在CODE上查看代码片派生到我的代码片
#!/usr/bin/envpython deffun(): f=open('/etc/motd','r') longest=0 allLines=[x.strip()forxinf.readlines()]#orallLineLens=[len(x.strip())forxinf] f.close() forlineinallLines: linelen=len(line) iflinelen>longest:#orlongest=max(allLineLens) longest=linelen returnlongest
新的方法:
使用生成器表达式替换列表解析和max()函数,并去掉文件打开模式(默认为读取):returnmax(len(x.strip())forxinopen('/etc/motd'))。
三、总结
(1)itertools模块被加入,更加支持了迭代器的应用,列表解析和生成表达式的内容可结合实例分析。
(2)若有不足,请留言,在此先感谢!