Python的流程控制语句
条件判断:if语句
语法格式:
if <条件判断1>:
<执行1>
elif <条件判断2>:
<执行2>
elif <条件判断3>:
<执行3>
else:
<执行4>
可以有零个或多个 elif
部分,以及一个可选的 else
部分。 关键字 'elif
' 是 'else if' 的缩写,适合用于避免过多的缩进。 一个 if
... elif
... elif
... 序列可以看作是其他语言中的 switch
或 case
语句的替代。
出现如下的错误:TypeError: 'int' object is not callable,是因为在前面,使用int = 123,定义了一个int变量,而这里又使用了int()函数,当Python中变量名和函数名重复时,就容易出现此错误。
if条件判读语句同其他语言的语法基本相同,注意的是if elif else语句后跟冒号:,后面的执行语句块要缩进。
这里主要关注的是条件判读语句,其结果一定要是布尔型。
空字符串,空列表、0,在布尔判断上都是假,非空的字符串、列表等,非0的其他数字都是真。
判断可以使用>,<,=,<=,>=,!=,可达使用逻辑连接符and,or,not等进行多条判断,使用in关键字判断是否在目标中存在
循环语句:while循环语句
语法:
while <判断条件>:
<循环体>
while循环,只要条件满足,就不断循环,条件不满足时退出循环。
同上面的if判断语句一样,任何非零整数都为真;零为假。这个条件也可以是字符串或是列表的值,事实上任何序列都可以;长度非零就为真,空序列就为假。标准的比较操作符的写法和 C 语言里是一样: < (小于)、 > (大于)、 == (等于)、 <= (小于或等于)、 >= (大于或等于)以及 != (不等于)。
循环体 是 缩进的 :在交互式命令行里,你得给每个缩进的行敲下 Tab 键或者(多个)空格键。实际上用文本编辑器的话,你要准备更复杂的输入方式;所有像样的文本编辑器都有自动缩进的设置。交互式命令行里,当一个组合的语句输入时, 需要在最后敲一个空白行表示完成(因为语法分析器猜不出来你什么时候打的是最后一行)。注意,在同一块语句中的每一行,都要缩进相同的长度。
打印斐波那契数列:
循环语句:for循环语句
Python中的for循环语句,与其他语言的语法存在明显的不同,也是其特色。 Python 中的 for 语句并不总是对算术递增的数值进行迭代(如同 Pascal),或是给予用户定义迭代步骤和暂停条件的能力(如同 C),而是对任意序列进行迭代(例如列表或字符串),条目的迭代顺序与它们在序列中出现的顺序一致。
如其他的语言for的语法一般是如下的形式:
for (起始值;终止值;步长)
for (i=0;i<10;i++)
而Python主要使用in关键字在可迭代对象中进行迭代,也就是顺序遍历每个可迭代对象中的元素,通过对可迭代对象的控制,来控制循环。
在遍历同一个集合时修改该集合的代码可能很难获得正确的结果。通常,更直接的做法是循环遍历该集合的副本或创建新集合。
如果确实需要遍历一个数字序列,内置函数 range()
会派上用场。它生成算术级数:
给定的终止数值并不在要生成的序列里;range(10)
会生成10个值,并且是以合法的索引生成一个长度为10的序列。range也可以以另一个数字开头,或者以指定的幅度增加(甚至是负数;有时这也被叫做 '步进')
要以序列的索引来迭代,您可以将 range() 和 len() 组合如下:
对于上面的例子,在大多数这类情况下,使用 enumerate() 函数比较方便,
enumerate
(iterable, start=0) 返回一个枚举对象。iterable 必须是一个序列,或 iterator,或其他支持迭代的对象。 enumerate()
返回的迭代器的 __next__()
方法返回一个元组,里面包含一个计数值(从 start 开始,默认为 0)和通过迭代 iterable 获得的值。
range() 所返回的对象在许多方面表现得像一个列表,但实际上却并不是。此对象会在迭代它时基于所希望的序列返回连续的项,但它没有真正生成列表,这样就能节省空间。
称这样对象为 iterable,也就是说,适合作为这样的目标对象:函数和结构期望从中获取连续的项直到所提供的项全部耗尽。 已经看到 for 语句就是这样一种结构,而接受可迭代对象的函数的一个例子是 sum():
break 和 continue 语句,以及循环中的 else 子句
break 语句,和 C 等其他语言中的类似,用于跳出最近的 for 或 while 循环.
运行上面的代码可以看到,打印出1~5后,紧接着打印END,程序结束。可见break的作用是提前结束循环。
有一个问题没有解决:在交互窗口下写循环代码,只能写到循环处,后面不能写了:
这里为什么语法错误???
continue,是在循环过程中,可以通过continue语句,跳过当前的这次循环,直接开始下一次循环。
上面的程序可以打印出1~10。但是,如果我们想只打印奇数,可以用continue语句跳过某些循环,使用下面的代码可以看到,打印的不再是1~10,而是1,3,5,7,9。可见continue的作用是提前结束本轮循环,并直接开始下一轮循环。
循环是让计算机做重复任务的有效的方法。break语句可以在循环过程中直接退出循环,而continue语句可以提前结束本轮循环,并直接开始下一轮循环。这两个语句通常都必须配合if语句使用。
有人建议:不要滥用break和continue语句。break和continue会造成代码执行逻辑分叉过多,容易出错。大多数循环都可以通过改写循环条件或者修改循环逻辑,去掉break和continue语句。
循环语句可能带有 else 子句;它会在循环耗尽了可迭代对象 (使用 for) 或循环条件变为假值 (使用 while) 时被执行,但不会在循环被 break 语句终止时被执行。
上例,while循环到了条件为假,这时执行了else语句,打印了else,并且执行完毕后,又执行了后续语句,打印end。
当循环是通过break跳出时,else语句不被执行。上面例子中就没有打印else。
上面是for循环中的else语句测试。
continue不影响。
利用else计算质数:
else是for的语法,与for配套,而不是与if配套。
pass 语句
pass 语句什么也不做。当语法上需要一个语句,但程序需要什么动作也不做时,可以使用它。例如:
>>> class MyEmptyClass:
... pass
pass 的另一个可以使用的场合是在你编写新的代码时作为一个函数或条件子句体的占位符,允许你保持在更抽象的层次上进行思考。 pass 会被静默地忽略:
>>> def initlog(*args):
... pass # Remember to implement this!
关于迭代:
如果给定一个list或tuple,可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。在Python中,迭代是通过for ... in来完成的,而很多其他语言比如C语言,迭代list是通过下标完成的。
Python的for循环抽象程度要高于C的for循环,因为Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。
list这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可迭代对象,无论有无下标,都可以迭代,比如dict就可以迭代:
因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。
由于字符串也是可迭代对象,因此,也可以作用于for循环:
所以,当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。
如果要对list实现类似下标循环怎么办?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
for循环里,同时引用了两个变量,在Python里是很常见的:
列表生成式
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11)):
但如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做?方法一是循环:
但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list:
写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来,十分有用。for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
还可以使用两层循环,可以生成全排列:
for循环可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value,因此,列表生成式也可以使用两个变量来生成list:
把一个list中所有的字符串变成小写:
使用列表生成式的时候,不能在最后的if加上else,这是因为跟在for后面的if是一个筛选条件,不能带else,否则就无法筛选了。
if可以写在前面,即在表达式中写,但if写在for前面必须加else,否则报错:
这是因为for前面的部分是一个表达式,它必须根据x计算出一个结果。因此,考察表达式:x if x % 2 == 0,它无法根据x计算出结果(无法计算出所有结果,能计算一部分),因为缺少else,必须加上else,加上else后,就能计算全部结果了。
可见,在一个列表生成式中,for前面的if ... else是表达式,而for后面的if是过滤条件,不能带else。
生成器:
通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。可以直接打印出list的每一个元素,但怎么打印出generator的每一个元素呢?如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。上面这种不断调用next(g)太繁琐死板,正确的方法是使用for循环,因为generator也是可迭代对象:
第一次调用,因为前面next的调用,generator已经计算到最后了,不能从头再来,所以打印是空的。通过for循环来迭代它,并且不需要关心StopIteration的错误。
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到,斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)就报错。
回到fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
第一次使用的fib(6),fib()是个普通函数,斐波那契数列是函数打印功能所打印的出来的,后面的done,是函数返回的值,然后for循环的是这个“done”,所以才出现上面的结果。使用fibg()函数,fibg是生成器,斐波那契数列是生成器返回的值,for的作用就相当于不停的执行next(),当超出范围时,实际上会抛出StopIteration的 错误,这时for不处理,直接结束,所以,这里的return “done”是执行不到的,也就没有done输出。
所以结论就是:用for循环调用generator时,拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
对于generator生成器的yield认识:从网上搜到的资料:(原创:冯爽朗 python中yield的用法详解——最简单,最清晰的解释)
========================================================================================================================
如果你还没有对yield有个初步分认识,那么你先把yield看做“return”,这个是最直观的,它首先是个return,普通的return是什么意思,就是在程序中返回某个值,返回之后程序就不再往下运行了。看做return之后再把它看做一个是生成器(generator)的一部分(带yield的函数才是真正的迭代器),好了,如果你对这些不明白的话,那先把yield看做return,然后直接看下面的程序,你就会明白yield的全部意思了:
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))
就这么简单的几行代码就让你明白什么是yield,代码的输出这个:
starting...
4
********************
res: None
4
我直接解释代码运行顺序,相当于代码单步调试:
1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)
2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环
3.程序遇到yield关键字,然后把yield想象成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果。(这里的重点是return的结果是yield关键字后面的表达式的值,这里给出了最简单的具体值4,实际应用中应该是一个表达式,最常用的应该是一个变量)
4.程序执行print("*"*20),输出20个*
5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None。(这里要注意,对于 res = yield 4这一条赋值语句,实际是分成多步操作的,一般是先计算右边的表达式,计算出结果,再赋值给左边的变量。而因为是yield的表达式,直接将yield关键字后面的表达式的计算的结果返回了,而yield整体本身的结果始终是None,即yield整体的结果是None,跟返回结果不同。next()只是执行到return 4这一步,相当于下图的返回a+b+c的值,下一次next(),是接着上次的执行,即要计算出yield 4这个整体表达式的值,就是None,然后赋值给res,所以,这种情况下,res将一直为None)
6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.(这里描述是不正确的,res:None是print函数打印出来的,4是return出来的,不是print函数打印出来的。)
到这里你可能就明白yield和return的关系和区别了,带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。
****************************************************************************************************************************************
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))
再看一个这个生成器的send函数的例子,这个例子就把上面那个例子的最后一行换掉了,输出结果:
starting...
4
********************
res: 7
4
先大致说一下send函数的概念:此时你应该注意到上面那个的紫色的字,还有上面那个res的值为什么是None,这个变成了7,到底为什么,这是因为,send是发送一个参数给res的,因为上面讲到,return的时候,并没有把4赋值给res,下次执行的时候只好继续执行赋值操作,只好赋值为None了,而如果用send的话,开始执行的时候,先接着上一次(return 4之后)执行,先把7赋值给了res,然后执行next的作用,遇见下一回的yield,return出结果后结束。
(这里的描述也是有点问题,send的参数不是发送给res的,send(value)是恢复执行并向生成器函数“发送”一个值。 value 参数将成为当前 yield 表达式的结果。 send()
方法会返回生成器所产生的下一个值,或者如果生成器没有产生下一个值就退出则会引发 StopIteration
。 当调用 send()
来启动生成器时,它必须以 None
作为调用参数,因为这时没有可以接收值的 yield 表达式。也就是说,send的参数是作为了yield整体表达式的值,原来默认是固定值None,现在用这个参数替换掉)
5.程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量。(应该是send把7这个参数传递给yield整体表达式,作为整体表达式的结果,然后赋值给了res)
6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环
7.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。
这就结束了,说一下,为什么用这个生成器,是因为如果用List的话,会占用更大的空间,比如说取0,1,2,3,4,5,6............1000
你可能会这样:
for n in range(1000):
a=n
这个时候range(1000)就默认生成一个含有1000个数的list了,所以很占内存。
这个时候你可以用刚才的yield组合成生成器进行实现,也可以用xrange(1000)这个生成器实现
yield组合:
def foo(num):
print("starting...")
while num<10:
num=num+1
yield num
for n in foo(0):
print(n)
输出:
starting...
1
2
3
4
5
6
7
8
9
10
xrange(1000):
for n in xrange(1000):
a=n
其中要注意的是python3时已经没有xrange()了,在python3中,range()就是xrange()了,你可以在python3中查看range()的类型,它已经是个
========================================================================================================
迭代器:
可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象:
而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。把list、dict、str等Iterable变成Iterator可以使用iter()函数:
为什么list、dict、str等数据类型不是Iterator?这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break