while 循环 一般格式: [python] view plain copy while <test>: <statements1> else <test>: <statements2> else为可选部分,当控制权离开循环而又没有碰到break语句时会执行。 [python] view plain copy >>> x = ‘spam‘ >>> while x : print (x,end=‘ ‘) x = x[1:] spam pam am m 注意:这里使用end = ‘ ‘关键字参数,使所有输出都出现在同一行,之间用空格隔开 ------------------------------------------------------------------------------------------------------------------------------------------------ break、continue、pass和循环else break:跳出最近所在的循环(跳过整个循环语句) continue:跳到最近所在循环的开头处(来到循环的首行) pass:什么事也不做,只是空占位语句 循环else块:只有当循环正常离开时才会执行(也就是没有碰到break语句) ------------------------------------------------------------------------------------------------------------------------------------------------ 一般循环格式 加入break和continue后,while的一般循环格式变为: [python] view plain copy while <test1>: <statements1> if <test2>:break if <test3>:continue else: <statements2> ------------------------------------------------------------------------------------------------------------------------------------------------ pass语句是无运算的占位语句,当语法需要语句并且还没有任何使用的语句可写时,就可以使用它。它通常用于为复合语句编写一个空的主体。例如,如果想写个无限循环,每次迭代什么也不做,就写个pass [python] view plain copy >>> while True :pass 因为主体只是空语句,Python陷入了死循环,这个程序并没有什么用处。以后将会看到它更有意义的用处。例如,忽略try语句所捕获的异常,以及定义带属性的空类对象,而该类实现的对象行为就像其他语言的结构和记录。pass有时指的是“以后会填上”,只是暂时用于填充函数主体而已 [python] view plain copy def func1(): pass def func2(): pass 我们无法保持函数体为空而不产生语法错误,因此,可以使用pass来替代 【另外:在Python3.0以后,程序中可以使用三个点...来代替pass,这样似乎更加简洁明了】 ------------------------------------------------------------------------------------------------------------------------------------------------ continue语句会立即跳到循环的顶端,即跳过本次循环,执行下一次循环 break 语句会立即离开循环 [python] view plain copy >>> while True: name = input(‘Enter name:‘) if name == ‘stop‘:break age = input(‘Enter age:‘) print(‘Hello‘,name,‘=>‘,int(age)**2) Enter name:Gavin Enter age:2 Hello Gavin => 4 Enter name:Spam Enter age:20 Hello Spam => 400 Enter name:stop ------------------------------------------------------------------------------------------------------------------------------------------------ 循环else 和循环else子句结合时,break语句通常可以忽略其他语言中所需要的搜索状态标志位。 例如,下列程序搜索大于1的因子,来决定正整数y是否为质数: [python] view plain copy >>> def judge(y): x = y//2 while x >1: if y % x ==0: print(y,‘has factor‘,x) break x -= 1 else: print(y,‘is prime‘) >>> judge(8) 8 has factor 4 >>> judge(19) 19 is prime >>> judge(119) 119 has factor 17 除了设置标志位在循环结束时进行测试外,也可以在找到因子时插入break。这样一来,循环else分句可以视为只有当没有找到因子时才会执行。如果你没有碰到break,该数就是质数。 【注意,当第一次判断while循环条件就不满足时,也就是一次循环也没有进行时,还是会执行else子句的】 ================================================================================= for循环 一般用法: [python] view plain copy for <target> in <object>: <statements> else: <statements> 当python运行for循环时,会逐个将序列对象中的元素赋值给目标,然后为每个元素执行循环主体。 for循环也可以用break、continue和else子句 完整格式如下: [python] view plain copy for <target> in <object>: <statements> if <test>:break if <test>:continue else: <statements> ------------------------------------------------------------------------------------------------------------------------------------------------ 基础应用: [python] view plain copy >>> for x in [‘spam‘,‘eggs‘,‘ham‘]: print(x,end= ‘ ‘) spam eggs ham 下面两个例子会计算列表得到所有元素的和与乘积。 [python] view plain copy >>> s = 0 >>> for x in [1,2,3,4]: s += x >>> x #这里x这个变量依然是有值的 4 >>> s 10 [python] view plain copy >>> p = 1 >>> for x in [1,2,3,4]: p *= x >>> p 24 在以后,还会知道Python有一些工具,可以自动对列表中的元素应用诸如‘+’和‘*‘类似的运算,但是使用for循环通常也一样简单 ------------------------------------------------------------------------------------------------------------------------------------------------ 其他数据类型 不止对于列表适用,for循环对任何序列都适用,for循环还可以应用于字符串和元组,这里就不举例了。 实际上,for循环甚至可以应用在一些根本不是序列的对象上,对于文件和字典也有效。 ------------------------------------------------------------------------------------------------------------------------------------------------ 在for 循环中的元组赋值 [python] view plain copy >>> T = [(1,2),(3,4),(5,6)] >>> for (a,b) in T: print(a,b) 1 2 3 4 5 6 [python] view plain copy >>> for x in T : print(x) (1, 2) (3, 4) (5, 6) 注意上述两个例子的区别。第一个例子可以看做元组解包的赋值运算 这种形式通常和我们将要介绍的zip调用一起使用,以实现并行遍历。在Python中,它通常还和SQL数据库一起使用,其中,查询结果表作为这里使用的列表这样的序列的序列而返回——外围的列表就是数据库表,嵌套的元组是表中的行,元组赋值和列对应 for 循环中的元组使得用items方法来遍历字典中的键和值变得很方便,而不必再遍历键并手动索引以获取值: [python] view plain copy >>> D = {‘a‘:1,‘b‘:2,‘c‘:3} >>> for key in D: print(key,‘=>‘,D[key]) b => 2 c => 3 a => 1 [python] view plain copy >>> list(D.items()) [(‘b‘, 2), (‘c‘, 3), (‘a‘, 1)] >>> for (key,value) in D.items(): print(key,‘=>‘,value) b => 2 c => 3 a => 1 注意for循环中的元组赋值并非一种特殊情况,这一点很重要;单词for之后的任何赋值目标在语法上都是有效的。尽管我们总是在for循环中手动地赋值以解包: [python] view plain copy >>> for both in T: a,b = both print(a,b) 1 2 3 4 5 6 ------------------------------------------------------------------------------------------------------------------------------------------------ Python3.0 在for循环中扩展的序列赋值 实际上,for循环中的循环变量真的可以是任何赋值目标,在这里,也可以使用Python3.0的扩展序列解包赋值语法,来提取序列中的序列的元素和部分。 扩展序列赋值可以参考这里 ------------------------------------------------------------------------------------------------------------------------------------------------ 嵌套for循环 这段代码会在对象列表中搜索每个键,然后报告其搜索结果: [python] view plain copy >>> items = [‘aaa‘,111,(4,5),2.01] >>> tests = [(4,5),3.14] >>> for key in tests: for item in items: if key == item: print(key,‘was found‘) break else: print(key,‘not found‘) (4, 5) was found 3.14 not found ================================================================================= 编写循环的技巧 1.内置range函数返回一系列连续增加的整数,可作为for中的索引 2.内置zip函数返回并行元素的元组的列表,可用于在for中内遍历整个序列。 ------------------------------------------------------------------------------------------------------------------------------------------------ 循环计数器:while和range range函数是通用的工具,可用在各种环境中,虽然range常用在for循环中来产生索引,但也可以用在任何需要整数列表的地方。在Python3.0中,range是一个迭代器,会根据需要产生元素,因此,我们需要将其包含到一个list调用中以一次性显示其结果: [python] view plain copy >>> list(range(5)),list(range(2,5)),list(range(0,10,2)) ([0, 1, 2, 3, 4], [2, 3, 4], [0, 2, 4, 6, 8]) 一个参数时,range会产生从零算起的整数列表,但其中不包括该参数的值; 如果传进两个参数,第一个视为下边界。 第三个选用参数可以提供步进值,使用时,Python会对每个连续整数加上步进值(步进值默认为1). range也可以是非正数或非递增的: [python] view plain copy >>> list(range(-5,5)) [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] >>> list(range(5,-5,-2)) [5, 3, 1, -1, -3] 从内部实现上来看,for循环以这种方式使用时,会自动处理迭代的细节。如果你真的想要明确得掌控索引逻辑,可以用while循环来实现: [python] view plain copy >>> s = ‘gavin‘ >>> i = 0 >>> while i<len(s): print(s[i],end = ‘ ‘) i+=1 g a v i n ------------------------------------------------------------------------------------------------------------------------------------------------ 非完备遍历:range和分片 一般情况下,在对序列进行遍历时,最好使用Python中的简单的for循环,不要使用while,并且不要在for循环中使用range调用,只将其视为最后的手段。 [python] view plain copy >>> for item in X:print(item) 然而利用range进行索引的用处是,我们可以通过控制range来实现特殊的遍历,例如,在遍历的过程中跳过一些元素 [python] view plain copy >>> S = ‘abcdefghijklmn‘ >>> for i in range(0,len(S),2):print(S[i],end = ‘ ‘) a c e g i k m 在这里,我们通过所产生的range列表,访问了字符串S中每隔一个的元素,要使用每隔两个的元素,可以把range的第三个参数改为3,以此类推。 然而,这可能不是如今Python中最理想情况下实现的技术,如果你真想跳过序列中的元素,可以用前面介绍的扩展的第三个限制值形式的分片表达式,例如,要使用S中每隔一个的字符串,可以用步进值2来分片: [python] view plain copy >>> for item in S[::2]:print(item,end=‘ ‘) a c e g i k m 结果是相同的,但对我们来说更容易编写,对其他人来说,更容易阅读。 ------------------------------------------------------------------------------------------------------------------------------------------------ 修改列表:range 可以使用range和for的组合的常见场合就是在循环中遍历列表时对其进行修改。例如,假设你因某种理由要为列表中的每个元素都加1,你可以通过简单的for循环来做,但结果可能不是你想要的: [python] view plain copy >>> L = [1,2,3,4,5] >>> for x in L: x+=1 >>> L [1, 2, 3, 4, 5] >>> x 6 这样并不行,因为修改的是循环变量x,而不是列表L. 要真的在我们遍历列表时对其修改,我们需要使用索引,让我们可以在遍历时替每个位置赋一个一个已更新的值。range/len组合可以替我们产生所需要的索引。 [python] view plain copy >>> L = [1,2,3,4,5] >>> for i in range(len(L)): L[i] +=1 >>> L [2, 3, 4, 5, 6] 用while循环也可以做,只是运行比较慢。 [python] view plain copy [x+1 for x in L] 这种形式的列表解析表达式也能做类似的工作,而且没有对最初的列表进行在原处的修改(我们可以把表达式的新列表对象赋值给L,但是这样不会更新原始列表的其他任何引用值)。因为这是循环的核心概念,我们将在以后对列表解析做一个完整的介绍。 ------------------------------------------------------------------------------------------------------------------------------------------------ 并行遍历:zip和map 内置zip函数可以让我们使用for循环来并行使用多个序列,在基本运算中,zip会取得一个或多个序列为参数,然后返回元组的列表,将这些序列中的并排的元素配成对 例如,假设我们要使用两个列表: [python] view plain copy >>> A = [1,2,3,4] >>> B = [11,22,33,44] 要合并这些列表中的元素,我们可以使用zip来创建一个元组对的列表(和range一样,zip在Python3.0中也是一个可迭代对象,因此,我们必须将其包含在一个list调用中以便一次性显示所有结果) [python] view plain copy >>> C = zip(A,B) >>> C <zip object at 0x0405BE18> >>> list(C) [(1, 11), (2, 22), (3, 33), (4, 44)] 这样的结果在其他环境下也有用,然而搭配for循环中,它就会支持并行迭代: [python] view plain copy >>> for (x,y) in zip(A,B): print(x,y,‘---‘,x+y) 1 11 --- 12 2 22 --- 24 3 33 --- 36 4 44 --- 48 zip可以接受任何类型的序列(其实就是任何可迭代的对象,包括文件),并且可以有两个以上的参数。例如 [python] view plain copy >>> T1,T2,T3 = (1,2,3),(4,5,6),(7,8,9) >>> T2 (4, 5, 6) >>> list(zip(T1,T2,T3)) [(1, 4, 7), (2, 5, 8), (3, 6, 9)] 当参数长度不同时,zip会以最短序列的长度为准来截断所得到的元组: [python] view plain copy >>> S1 = ‘abc‘ >>> S2 = ‘xyz123‘ >>> list(zip(S1,S2)) [(‘a‘, ‘x‘), (‘b‘, ‘y‘), (‘c‘, ‘z‘)] ------------------------------------------------------------------------------------------------------------------------------------------------ 使用zip构造字典 前面介绍过,当键和值的集合必须在运行时计算时,这里所用的zip调用也可用于产生字典,并且非常方便。 假设有下列的键和值的列表: [python] view plain copy >>> keys = [‘spam‘,‘eggs‘,‘toast‘] >>> vals=[1,2,3] 将这些列表变成字典的一种做法就是将这些字符zip起来,并通过for循环并行步进处理: [python] view plain copy >>> list(zip(keys,vals)) [(‘spam‘, 1), (‘eggs‘, 2), (‘toast‘, 3)] >>> D2 = {} >>> for (key,val) in zip(keys,vals): D2[key] = val >>> D2 {‘toast‘: 3, ‘spam‘: 1, ‘eggs‘: 2} 不过,在Python2.2和后续版本中,可以完全跳过for循环,直接把zip过的键/值列表传给内置的dict构造函数: [python] view plain copy >>> D3 = dict(zip(keys,vals)) >>> D3 {‘toast‘: 3, ‘spam‘: 1, ‘eggs‘: 2} ------------------------------------------------------------------------------------------------------------------------------------------------ 产生偏移和元素:enumerate 之前,我们讨论过通过range来产生字符串中元素的偏移值,而不是那些偏移处的元素,不过,在有些程序中,我们两者都需要:需要的元素以及这个元素的偏移值。 可以按照下面这个简单的例子做: [python] view plain copy >>> S = ‘spam‘ >>> offset = 0 >>> for item in S: print(item,‘appears at offset‘,offset) offset += 1 s appears at offset 0 p appears at offset 1 a appears at offset 2 m appears at offset 3 不过内置函数enumerate可以为我们做这件事情: [python] view plain copy >>> S = ‘spam‘ >>> for (offset,item) in enumerate(S): print(item,‘appears at offset‘,offset) s appears at offset 0 p appears at offset 1 a appears at offset 2 m appears at offset 3 可以看到: [python] view plain copy >>> enumerate(S) <enumerate object at 0x03602FA8> >>> list(enumerate(S)) [(0, ‘s‘), (1, ‘p‘), (2, ‘a‘), (3, ‘m‘)] enumerate函数返回一个生成器对象:这种对象支持接下来要学习的迭代协议。简而言之,这个对象有一个__next__方法,由下一个内置函数调用它,并且在循环中每次迭代的时候它会返回一个(index,value)的元祖。我们可以在for循环中通过元组赋值将元组解包(很像是使用zip) [python] view plain copy >>> E = enumerate(S) >>> E <enumerate object at 0x03602F80> >>> next(E) (0, ‘s‘) >>> next(E) (1, ‘p‘) >>> next(E) (2, ‘a‘) [python] view plain copy >>> [c*i for (i,c) in enumerate(S)] [‘‘, ‘p‘, ‘aa‘, ‘mmm‘]