Python学习笔记整理(十一)Python的while和for循环
while语句,提供了编写通用循环的一种方法,而for语句是用来遍历序列对象内的元素,并对每个元素运行一个代码块。break,continue用在循环内,跳出整个循环或者跳出一次循环。
一、while循环
1、一般格式
格式:首行以及测试表达式,有一列或多列缩进语句的主体以及一个选用的else部分(控制权离开循环时而没有碰到break语句时会执行)
python会一直计算开投的测试,然后执行循环主体内的语句,直到测试返回假值为止。
while <test>:
<statements1>
else:
<statements2>
2、例子
>>> while True:
... print "Type Ctrl+C to stop!"
>>> while x:
... print x,
... x=x[1:]
...
diege iege ege ge e
注意 print末尾的逗号,会使所有输出都出现在同一行。
>>> a,b=0,10
>>> while a<b:
... print a,
... a+=1
...
0 1 2 3 4 5 6 7 8 9
Python并没有其他语言中所谓的"do until”循环语句,不过我们可以在循环主体底部以一个测试和break来实现类似的功能。
while True:
do something
if exitTest():break
3、对比shell的while语句
while 命令
do
命令1
命令2
done
在系统管理时常用与逐行读取一个文件并处理。
while read line
do
echo $line
done < /etc/rc.conf
shell中还有一个类似while的循环until
until 条件
do
命令1
命令2
done
EG:
IS_ROOT=`who |grep root`
until [ "$IS_ROOT" ]
do
echo 'root online'
sleep 2
done
二、 break continue pass和循环的else
break
跳出最近所在的循环(跳出整个循环语句)
continue
跳到最近所在循环的开头处(来到循环的首行,跳过本次循环)
pass
什么事也不做,只是空占位语句
循环else块
只有当循环正常离开时才会执行(也就是没有碰到break语句)
1、一般循环格式
加入break和continue语句后,while循环的一般格式如下:
while <test>:
<statements1>
if <test2>:break
if <test3>:continue
if <test4>:pass
else:
<statements2>
break和continue可以出现在while(或for)循环主体的任何地方,但通常会进一步嵌套在if语句中,根据某些条件来采取对应的操作。
2、列子
pass
>>> while 1:pass
...
pass可用于空类,有时有指的是"以后会填上”,只是暂时用于填充函数主体而已:
>>> def func1():
... pass
continue
continue语句会立即跳到循环的顶端,开始下一次循环。
>>> while x:
... x=x-1
... if x%2!=0:continue
... print x,
...
8 6 4 2 0
这个例子中,如果是奇数就返回循环顶部,不会打印.是偶数就打印。
这个下面这个结果一样
>>> while x:
... x=x-1
... if x%2==0:
... print x,
...
8 6 4 2 0
注意这两个例子的print位置,第一个print是属于while块的,测试不通过下执行,测试通过就回到循环顶端,第二个是属于if块的,只有测试通过才打印
>>> while x:
... x=x-1
... if x%2==0:
... print x,
...break
break语句会立刻离开循环。碰到break,位于其后的循环代码都不会执行。
while 1:
name=raw_input("Enter name:")
if name=='stop':break
age=raw_input("Enter age:")
print 'Hello',name,'=>',int(age)**2
当age不输入任何值时会报错。可以使用try解决
while 1:
name=raw_input("Enter name:")
if name=='stop':break
age=raw_input("Enter age:")
try:
print 'Hello',name,'=>',int(age)**2
except:
print 'Please input age!'
else 只有当循环正常离开时才会执行(也就是没有碰到break语句【break的条件不满足】)
>>> while x:
... x=x-1
... print x
... else:
... print "over"
...
9
8
7
6
5
4
3
2
1
0
over
>>> x=10
>>> while x:
... x=x-1
... if x==5:break
... print x
... else:
... print "over"
...
9
8
7
6
三、for循环
for循环在Python中是一个通用的序列迭代器:可以遍历任何有序的序列对象内的元素。for语句可用于字符串,列表,元组,其他内置可迭代对象以及之后我们能够通过类所创建的新对象。
1、一般格式
Python for循环的首行定义了一个赋值目标(或【一些目标】),以及你想遍历的对象,首行后面是你想重复的语句块(一般都有缩进)
for <target> in <object>:
<statements>
else:
<statements>
当ptyhon运行for循环时,会逐个将序列对象中的元素赋值给目标,然后为每个元素执行循环主体。循环主体一般使用赋值的目标来引用序列中当前的元素,就好像那事遍历序列的游标。
for首行中用作赋值目标的变量名通常是for语句所在作用于的变量(可能是新的)。这个变量名没有什么特别的,甚至可以在循环主体中修改。但是当控制权
再次回到循环顶端时,就会自动被设成序列的下一个元素。循环之后,这个变量一般都还是引用了最近所用过的元素,也就是序列中最后的元素,除非通过一个
break语句退出了循环。
for语句也支持一个选用的else块,它的工作就像在while循环中一样:如果循环离开时没有碰到break语句,就会执行(也就是序列所有元素都被访问过了)
break和continue语句也可用在for循环中,就像while循环那样。for循环完整的格式如下:
for <target> in <object>:
<statements>
if <test>:break
if <test>:conitnue
else:
<statements>
对比shell的for循环
for 变量名 in 列表
do
命令1
命令1
done
for i in 1 2 3
do
echo $i
don
for i in `ls -1 /root`
do
echo $i
done
2、例子
1)基本应用
>>> x=[1,2,3,4,5]
>>> for i in x:
... print i
...
1
2
3
4
5
>>> for i in x:
... if i==3:break
... print i
...
1
2
>>> for i in x:
... print i
... if i==3:break
...
1
2
3
注意if语句的位置
> D={'name':['diege','lily','kelly'],'class':2012,'number':48}
>>> for i in D:
... print i,'=>',D[i]
...
number => 48
name => ['diege', 'lily', 'kelly']
class => 2012
多层
>>> for i in D:
... if type(D[i])==list:
... for l in D[i]:
... print l
... else:
... print D[i]
...
48
diege
lily
kelly
2012
for元组赋值
首行定义了一个赋值【一些目标】
>>> T=[(1,2),(3,4),(5,6)]
>>> for (a,b) in T:
... print a,b
...
1 2
3 4
5 6
for循环嵌套
遍历一个字典(或列表)包括字典,列表,元组的的语句
D={'game':'sjgame','version':[1.0,1.1,1.2,1.3],'useid':(1000,1001,1002,1003,1004),'character':{'WS':'wushi','FS':'fashi','MS':'moshi
'},'CP':'ice'}
for i in D:
if type(D[i])==list:
for l in D[i]:
print l
elif type(D[i])==tuple:
for t in D[i]:
print t
elif type(D[i])==dict:
for d in D[i]:
print d
else:
print D[i]
代替break的
>>> items=['diege',999,(3,7),1.3]
>>> tests=[(3,7),3.14]
>>> for key in tests:
... for item in items:
... if key==item:
... print key,'was found'
... break
... else:
... print key,'not found!'
...
(3, 7) was found
3.14 not found
有点类似查找的功能。
收集两个序列中相同元素
>>> seq1='diege'
>>> seq2='decgl'
>>> res=[]
>>> for x in seq1:
... if x in seq2:
... res.append(x)
...
>>>
>>> res
['d', 'e', 'g', 'e']
准确的说是显示seq1和seq2都用的在seq1一次顺序。
3、为什么要在意“文件扫描”
循环读取文件:while在文件结尾时使用break
>>> fl=open('/etc/rc.conf')
>>> while True:
... char=fl.read(1)
... if not char:break
... print char,
MS for循环打印出来的字体正常点,呵呵。
>>> for char in open('/etc/rc.conf'):for char in open('/etc/rc.conf').read():的缩写
... print char
...
使用while按行处理
>>> fl=open('/etc/rc.conf')
>>> while True:
... line=fl.readline()
... if not line:break
... print line,
...
按行读取文件文件时,for循环是最容易编写及执行最快的选择
>>> for line in open('/etc/rc.conf'):#默认read()
... print line
...
>>> for line in open('/etc/rc.conf').readlines():
... print line
...
readlines会一次把文件载入到行字符串的列表
>>> for line in open('/etc/rc.conf').xreadlines():
... print line
...
xreadlines则是按需求加载文字列,从而避免大型文件导致内存溢出。
每个步骤所读取的数据越多,程序会员越快。
四、迭代器:初探
for循环可以用在任何【可迭代的对象】。这些迭代工具包括了for循环,列表解析,in成员关系测试,以及map内置函数等。
可迭代对象:如果对象是实际保存的序列,或者可以在迭代工具环境中(如for循环)一次产生一个对象的结果,就被看作是可迭代。
总之,可迭代对象包括实际序列和按照需求而计算的虚拟序列。
1、文件迭代器
文件对象有个方法名为readline,可以一次从一个文件中读取一行文本,每次调用readline方法时,就会前进到下一列,到文件末尾时,就会返回空字符串,可以通过它来检查从而跳出循环。文件对象有另外一个方法next,差不多用有相同的效果。
>>> fl=open('/etc/rc.conf')
>>> fl.next()
'#apache22_enable="YES"\n'
>>> fl.next()
'# -- sysinstall generated deltas -- # Tue Feb 14 10:08:27 2012\n'
两者的区别在于,达到文件末尾时,next是引发内置的StopIteration异常,而不是返回空字符串。
这个接口就是python所谓的迭代协议:有next方法的对象会前进到下一个结果,而在一些列结果的末尾时,则会引发StopIteration.
在Python中,任何这类对象都认为是可迭代的。任何这类对象也能以for循环或其他迭代工具遍历,因为所有迭代工具内部工作起来都是在每次迭代中调用next,并且捕捉StopIteration异常来确定何时离开。
逐行读取文本的最佳方式就是根本不要去读取,其替代的方法就是,让for循环在每轮自动调用next从而前进到下一行。
>>> for line in open('/etc/rc.conf'):
... print line.upper(),
逐行读取文件并转换为大写,注意没有可以从文件中读取内容。
这首读取文本文件的最佳方式,最简单,运行最快,内存使用情况最好。
>>> for line in open('/etc/rc.conf').readlines:
这种方式一次把文件都加载到内存。
2、其他内置类型迭代器
>>> D={'a':1,'b':2,'c':3}
>>> for key in D.keys():
... print key,D[key]
...
a 1
c 3
b 2
新版无需使用keys方法
>>> for key in D:
... print key,D[key]
...
a 1
c 3
b 2
3、其他迭代环境
列表解析,in成员关系,map内置函数以及其他内置工具(如sorted,sum)
>>> [line.upper() for line in open('/etc/rc.conf')]
>>> upper=[line.upper() for line in open('/etc/rc.conf')]
>>> map(str.upper,open('/etc/rc.conf'))
map对迭代对象中每一个元素都应用一个函数调用,类似于列表解析,有局限性,因需要函数,而不是任意表达式。
>>> sorted(open('/etc/rc.conf')) #这个工具排序了,较新的内置函数,采用了迭代协议。应用于任何可迭代的对象上。
>>> sum([3,5,6,9]) #sum调用会计算任何可迭代对象内所有数字的和
23
而如果可迭代对象中的任何元素或者全部元素为True,内置函数就会返回True:all全部元素为True才返回True,有一个元素不为
True,都会返回flase
>>> any(['diege','','ni'])
True
>>> any(['diege','',''])
True
>>> all(['diege','','ni'])
False
>>> all(['diege','a','ni'])
True
其他工具:list和tuple内置函数(从可迭代对象创建新的对象),字符串join方法(在可迭代对象内字符串之间放入子字符串),以及序列赋值语句等。
>>> list(open('/etc/rc.conf'))
>>> tuple(open('/etc/rc.conf'))
>>> '&&'.join(open('/etc/rc.conf'))
>>> a,d,c,d=open('/etc/rc.conf')
4、用户定义的迭代器
yield语句可以把用户定义的函数变成可迭代的对象。如果的列表解析也可以通过生成器表达式支持次协议,而用户定义的类
也能通过__iter__或__getitem__运算符重载方法而变成迭代对象。用户定义的迭代器可以在这里的任何迭代环境中使用任意对象和运算。
五、编写循环的技巧
遍历序列时,首选for循环,for循环包括多数计数器式的循环,for比while容易写,执行时也比较快。
Python提供了两个内置函数,在for循环内定制迭代:
* 内置range函数返回连续整数列表,可作为for中的索引。
>>> range(5)
[0, 1, 2, 3, 4]
>>> range(5,10)
[5, 6, 7, 8, 9]
>>> range(5,10,2)
[5, 7, 9]
*内置 zip函数返回并行的元素元祖的列表,可用于在for中遍历数个序列
1、循环计算器:while和range
>>> range(5),range(2,5),range(3,10,2)
([0, 1, 2, 3, 4], [2, 3, 4], [3, 5, 7, 9])
步进为负时候为递减,默认递增
>>> range(5,-5,-1)
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]
range最常用于在for循环中,提供一种简单的方法,重复特定次数的动作。
>>> for i in range(3):
... print i,'pythons'
...
0 pythons
1 pythons
2 pythons
>>> for i in range(0,10,2):
... print i
...
0
2
4
6
8
如果真的要明确地掌控索引逻辑,可以用while循环来实现。
>>> X='diege'
>>> i=0
>>> while i<len(X):
... print X[i];i+=1
...
d
i
e
g
e
也可以使用for进行手动索引,也就是用range产生用于迭代的索引的列表
>>> range(len(X))
[0, 1, 2, 3, 4]
>>> for i in range(len(X)):print X[i],
...
d i e g e
2、非完备遍历:range
尽量使用简单的for循环,不要用while,并且不要在for循环中使用range调用,只将其视为最后的选择,更简单的办法总是更好。
>>> for i in X:
... print i
...
d
i
e
g
e
使用range可以做更特殊的遍历种类。
>>> S='rhrhrfxvlwsocv'
>>> for i in range(0,len(S),2):print S[i]
...
r
r
r
x
l
s
c
更好的办法
>>> for x in S[::2]:print x
...
r
r
r
x
l
s
c
3、修改列表:range
可以使用range和for的组合的常见场合就是在循环中遍历列表时并对其进行修改。如某些原因某种理由要为列表中每个元素都加1.
>>> L=[1,2,3,4,5]
>>> L
[1, 2, 3, 4, 5]
>>> for i in range(len(L)):
... L[i]+=1
...
>>> L
[2, 3, 4, 5, 6]
4、并行遍历:zip 和map
>>> L1=[1,2,3,4]
>>> L2=[5,6,7,8]
>>> zip(L1,L2)
[(1, 5), (2, 6), (3, 7), (4, 8)]
>>> for (x,y) in zip(L1,L2):
... print x,y,'--',x+y
...
1 5 -- 6
2 6 -- 8
3 7 -- 10
4 8 -- 12
zip从两列或者多列中提取出来元素配对。
>>> T1,T2,T3=(1,2,3),(4,5,6),(7,8,9)
>>> zip(T1,T2,T3)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
当参数长度不同时,zip会以最短序列的长度为准来截断所得到的元组:
>>> S1='ABC'
>>> S2='xyz123'
>>> zip(S1,S2)
[('A', 'x'), ('B', 'y'), ('C', 'z')]
内置map函数,用类似的方式把序列的元素配对起来,但是如果参数长度,不同则会为较短的序列用None补齐。
>>> map(None,S1,S2)
[('A', 'x'), ('B', 'y'), ('C', 'z'), (None, '1'), (None, '2'), (None, '3')]
使用zip构造字典
>>> keys=['name','age','class']
>>> vals=['diege',18,2012]
>>> zip(keys,vals)
[('name', 'diege'), ('age', 18), ('class', 2012)]
前面学过dict()函数,可以将k:v组成元组按列表的形式传送给disc()产生字典。
而zip可以掺产生这种格式的列表。故可以使用
dict(zip(keys,vals)) 构造字典
>>> dict(zip(keys,vals))
{'age': 18, 'name': 'diege', 'class': 2012}
>>> D5=dict(zip(keys,vals))
5、产生偏移和元素:enumerate
enumerate内置函数,同时产生偏移和元素
>>> S='diege'
>>> for (offset,item) in enumerate(S):
... print item,offset
...
d 0
i 1
e 2
g 3
e 4
这个方法有个next方法,每次遍历列表时,会返回一个(index,value)的元组,而我们能在for中通过元组赋值运算将其分解。
>>> E=enumerate(S)
>>> E.next()
(0, 'd')
>>> E.next()
(1, 'i')
>>> E.next()
(2, 'e')
>>> E.next()
(3, 'g')
>>> E.next()
(4, 'e')
>>> E.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
六、列表解析:
>>> L=[1,2,3,4,5]
>>> for i in range(len(L)):
... L[I]+=10
... L
>>> L
[1, 2, 3, 4, 5]
这样解析方式已经过时,这里用单个表达式取代循环,来产生所需要的结果列表
>>> L=[1,2,3,4,5]
>>> L=[x+10 for x in L]
>>> L
[11, 12, 13, 14, 15]
1、列表解析基础
列表解析是写在方括号中的,因为它毕竟是一种创建新的列表的方式。它是我们编写的任何表达式开始,而该表达式中使用了一个我们所编写循环的变量(x+10)。后面所接的就是for循环的首行,指出循环变量的名称以及可迭代的对象(for x in L)
[【表达式】 for x in L【for循环行首】]
技术角度,列表解析绝不是必须要求。因为可以使用for循环,在遍历的过程中把表达式结果加在列表上。
>>> res=[]
>>> L
[11, 12, 13, 14, 15]
>>> for x in L:
... res.append(x+10)
...
>>> L
[11, 12, 13, 14, 15]
不过列表解析写起来更简明,这种创建结果列表的编码模式在Python中是很常见的工作。而且运行更快,是以C语言的速度执行。
2、对文件使用列表解析
>>> f=open('/etc/rc.conf')
>>> lines=f.readlines()
>>> lines
['#apache22_enable="YES"\n', '# -- sysinstall generated deltas -- # Tue
Feb 14 10:08:27 2012\n', 'ifconfig_em1="inet 192.168.1.200 netmask
255.255.255.0"\n', '#ifconfig_em0="DHCP"\n', '# -- sysinstall generated
deltas -- # Tue Feb 14 10:09:24 2012\n', 'ifconfig_em1="inet
192.168.1.200 netmask 255.255.255.0"\n',
'#hostname="ofreebsd.corp.icee.cn"\n',
'hostname="ofreebsd.skylog.cn"\n', '# -- sysinstall generated deltas -- #
Tue Feb 14 10:12:04 2012\n', 'sshd_enable="YES"\n',
'#mysql_enable="YES"\n']
列表中包含的有换行符,如果想去掉换行符怎么办呢?
想到字符串的rstrip方法,但是怎么应用呢?也可以使用(line[:-1]也可以
>>> lines=[line.rstrip() for line in lines]
>>> lines
['#apache22_enable="YES"', '# -- sysinstall generated deltas -- # Tue
Feb 14 10:08:27 2012', 'ifconfig_em1="inet 192.168.1.200 netmask
255.255.255.0"', '#ifconfig_em0="DHCP"', '# -- sysinstall generated
deltas -- # Tue Feb 14 10:09:24 2012', 'ifconfig_em1="inet
192.168.1.200 netmask 255.255.255.0"',
'#hostname="ofreebsd.corp.icee.cn"', 'hostname="ofreebsd.skylog.cn"', '#
-- sysinstall generated deltas -- # Tue Feb 14 10:12:04 2012',
'sshd_enable="YES"', '#mysql_enable="YES"']
如果是打开文件读取文件,完全可以不用事先打开,可以在表达式内开启它。
>>> lines=[line.rstrip() for line in open('/etc/rc.conf')]
3、扩展列表解析语句
列表解析在实际使用中可以更高级。表达式中嵌套的for循环可以结合一个if分句来过滤测试不为真的结果元素,这可是一项有用的扩展功能。
重复上一个例子,但我们只需开头不为#的文字行。
>>> lines=[line.rstrip() for line in open('/etc/rc.conf') if line[0]!='#']
>>> lines
['ifconfig_em1="inet 192.168.1.200 netmask 255.255.255.0"',
'ifconfig_em1="inet 192.168.1.200 netmask 255.255.255.0"',
'hostname="ofreebsd.skylog.cn"', 'sshd_enable="YES"']
更复杂的情况,列表解析也可以含有嵌套循环,写成一系列的for分句。实际上,其完整的语句可接纳任意数目的for分句,而每个分区都可以结合一个可选的if分句
>>> [x+y for x in 'abc' for y in 'lmn']
['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']
对一个字符串中的每个x,以及另一个字符串中的每个y,创建x+y合并的列表。收集两个字符串字符的排列组合