前言
我们在高中数学就学过,一段程序之所以能够按照我们预想的步骤执行,就是因为有流程控制语句。也就是表示条件判断的if...else
语句,表示循环的for循环以及while语句。这一节我们来简单了解一下if...else
语句的执行机制,for循环的常见用法以及不那么常见的高级特性,while语句的使用情景。
条件判断
我们经常会碰到一种情况,就是在满足某个条件的时候做A事,反之做B事。 比如某个网站上的某些内容支队vip用户开放,那么在用户访问这些内容的时候,我们需要对用户对身份进行判断。
1 | if current_user.is_vip(): |
根据python的缩进规则,缩进一样if
和else
属于同一层级,实际上,在python不会经常出现条件语句嵌套的情况,一般都可以通过elif
语句解决。比如我们要对用户的年龄进行判断,根据用户的年龄推荐不同的内容给他:
1 | name = current_user.name |
值得注意的是,当if语句有一个为真时,其余的语句就不会执行了。在写很多elif
的时候尤其要注意这一点,避免程序的逻辑出错。
循环
python中有两种循环语句,一个是for
,一个是while
,前者是在要对一个数据集合,可迭代对象,或者generator遍历的时候使用,后者只是重复执行某个操作直到执行条件不满足。
while涉及到的东西比较简单,所以先讲while语句。
while
一个简单的例子就是:假设现在有一个活动是,前一百个满足条件的报名者,可以有礼物。那么主要代码是:
1 | n = 0 |
虽然上述逻辑也可以用for
循环实现,但是用while是更清楚简洁的。
for
第二钟循环语句是for
循环,一般当程序有用来迭代的数据集合的时候,会更倾向于使用for
而不是while
。 比如打印一个List
或者tuple
中的元素。
1 | names = ['zhu', 'hai', 'hao'] |
或者我们还利用range()
来达到类似于在c/c++
中for
循环的效果:
1 | for(int i = 0; i < 10; i++) { |
即换成python版本是:
1 | for i in range(10): |
break和continue
如果想要提前退出循环,用break语句即可。比如在上面遍历List
的例子中,如果想要在找到为hai的名字之后就退出循环,就是:
1 | names = ['zhu', 'hai', 'hao'] |
如果想略过其中某些循环,那么用continue即可。比如我们想要打印1~100的偶数,那么就要跳过中间的奇数,也就是:
1 | for i in range(100): |
循环深入
下面的内容会涉及到函数的一些东西,所以如果看不懂的同学可以看完函数之后再回来看。
迭代器
前面讲过了for
循环可以遍历list
的所有元素,dict
的所有key
, 还有range
对象。实际上还可以遍历字符串的所有字符,文件的每一行等等。
1 | for i in 'python'': |
实际上所有这些可以用for
循环语句遍历的对象都有一个统一的称呼叫做可迭代对象 – iterable objects。所有的可迭代对象都可以返回一个迭代器 – iterator,这个迭代器是一个惰性计算序列,可以作用于next()
函数而返回下一个值。for
循环实际上做的事情也就是不断的调用next
函数以返回下一个值,因此上面的代码也就等同于:
1 | x = 'python' |
list
, str
本身只是可迭代,而并不是迭代器。上面的iter()
函数是python内置的函数,用来创建iterator
。可以通过type()
函数查看y
的类型:
1 | >>> type(y) |
除了用iter
函数外,还可以自己创建一个自定义的iterator
或者使用itertools
中的函数来创建特殊的iterator
。从本质上来讲,只要有__next__()
函数和__iter__()
函数实现的对象就是一个iterator
。
有的同学会问了,为什么要用迭代器,直接用for
迭代相应的list不就好了吗?可以看出,迭代器是一个可以无限长的惰性序列,你可以不断调用next()
函数获得下一个值,如果是从list之类转化而来,那么当获取到最后一个元素之后再调用next()
函数就会抛出StopIteration
的错误。很多时候我们不确定会用到一段序列的多少元素,那么如果本身序列很长但是事后用到的元素又很少,那么对于内存就是一种浪费,所以可以先描述一段序列的生成方法,只有调用next()
函数的时候才计算下一个值。
我们可以实现一个能够打印Fibonacci数列的迭代器。
1 | class PrintFib: |
然后就可以这样使用:
1 | >>> f = PrintFib() |
显然,我们不可能把所有的斐波那契数都存放在list
中,这个时候用惰性计算就可以获取任意一个斐波那契数。
itertools
python还内置了很多有意思的函数可以通过list
创建有特殊用途的迭代器。
比如chain()
函数可以将多个迭代器合并成一个迭代器:
1 | In [55]: for i in chain('hello', 'world'): |
izip()
函数可以通过多个输入的迭代器组合对应元素成tuple
:
1 | In [59]: for i in zip([1, 2, 3], ['a', 'b', 'c'], ['c', 'd', 'e']): |
islice()
函数可以截取输入迭代器的一部分作为一个新的迭代器返回:
1 | In [6]: for i in islice(f,0, 5): |
更多的函数可以参考官方文档
生成器
通过编写类的方式我们可以自定义迭代器,但是有时候我们的生成序列的逻辑可能很简单,通过编写类的方式实现会有很多无用的代码。因此生成器可能使用起来更加顺手。
生成器有两种,一个是生成器函数,通过yield
关键字返回当前值。一种是生成器表达式,当我们的逻辑简单到编写函数都多余的时候,不妨使用生成器表达式使代码更加优雅。
还是以斐波那契序列为例子,这次用生成器函数来实现:
1 | In [60]: def fib(): |
生成器表达式和列表推导相似,只不过将后者的[]
换成()
,比如我们创建一个生成器包含100以内的平方数:
1 | In [69]: g = (x*x for x in range(10)) |
从上面的代码我们坏可以看出,我们既可以通过next()
来获取下一个值,也可以用for
循环,而且二者是相通的。
最后
最后我们用一张图来总结上面一些重要概念的关系:
下一篇文章是python中的函数。