你竟任着刚硬不悔改的心,为自己积蓄忿怒,以致神震怒,显他公义审判的日子来到。他必照各人的行为报应各人。凡恒心行善,寻求荣耀、尊贵和不能朽坏之福的,就以永生报应他们;惟有结党不顺从真理,反顺从不义的,就以忿怒、恼恨报应他们。(ROMANS 2:7-8)

迭代

Bill正在介绍他的项目,嘴里不断蹦出“loop、iterate、traversal、recursion”这些单词,夹杂在汉语汇总。旁边的小白们,都瞠目结舌,“不明觉厉”,心中的敬佩油然而生,

其实,Bill不是真正的大牛,我见过真牛的,他们绝对不会这么说的,他们通常总是轻描淡写,不管我认为多么麻烦的,他们都是举重若轻地解决:

“你就循环一下,然后来个迭代”,

“最后只要花几分钟写个递归就好了”

然后再问“这个你懂吗?”。

哦,这就是真正牛X的程序员。虽然还是不懂。

所以,我们还是老老实实地先搞清楚这些名词再说别的:

  • 循环(loop),指的是在满足条件的情况下,重复执行同一段代码。比如,while语句。
  • 迭代(iterate),指的是按照某种顺序逐个访问列表中的每一项。比如,for语句。
  • 递归(recursion),指的是一个函数不断调用自身的行为。比如,以编程方式输出著名的斐波纳契数列。
  • 遍历(traversal),指的是按照一定的规则访问树形结构中的每个节点,而且每个节点都只访问一次。

对于这四个听起来高深莫测的词汇,其实前面,已经涉及到了一个——循环(loop),本节主要介绍一下迭代(iterate),在网上google,就会发现,对于迭代和循环、递归之间的比较的文章不少,分别从不同角度将它们进行了对比。这里暂不比较,先搞明白Python中的迭代。

当然,迭代的话题如果要说起来,会很长,本着循序渐进的原则,这里介绍比较初级的。

逐个访问

在Python中,访问对象中每个元素,可以这么做:(例如一个list)

>>> lst = ['q', 'i', 'w', 's', 'i', 'r']
>>> for i in lst:
...     print i,        #Python 3: print(i, end='')
... 
q i w s i r

除了这种方法,还可以这样:

>>> lst_iter = iter(lst)    #对原来的list实施了一个iter()
>>> lst_iter.next()         #Python 3,请用:lst_iter.__next__()
'q'
>>> lst_iter.next()
'i'
>>> lst_iter.next()
'w'
>>> lst_iter.next()
's'
>>> lst_iter.next()
'i'
>>> lst_iter.next()
'r'
>>> lst_iter.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

iter()是一个内建函数,其含义是:

>>> help(iter)
Help on built-in function iter in module builtins:

iter(...)
    iter(iterable) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.

iter()函数返回的是一个迭代器对象。关于迭代器对象,本教程会有专门讲授。

>>> type(lst_iter)
<type 'listiterator'>        #Python 3:  <class 'list_iterator'>

所有的迭代器对象,在Python 2中有一个next()方法,在Python 3中,相应的修改为了__next__()。所以使用不同版本,要注意一下这个方法名称的变更。

迭代器,当然是可迭代的。本教程已经介绍过如果判断一个对象是否为可迭代对象的方法了。

在上面的举例中,next()或者__next__()就是要获得下一个元素,但是做为一名优秀的程序员,最佳品质就是“懒惰”,当然不能这样一个一个地敲啦,于是就:

>>> while True:
...     print lst_iter.next()        #Python 3: print(lst_iter.__next__())
... 
Traceback (most recent call last):  
  File "<stdin>", line 2, in <module>
StopIteration

先不管错误,再来一遍。

>>> lst_iter = iter(lst)               
>>> while True:
...     print lst_iter.next()        #Python 3: print(lst_iter.__next__())
... 
q                                      
i
w
s
i
r
Traceback (most recent call last):      #读取到最后一个之后,停止循环
  File "<stdin>", line 2, in <module>
StopIteration

看上面的演示的例子会发现,如果用for来迭代,当到末尾的时候,就自动结束了,不会报错。如果用next()或者__next__(),当最后一个完成之后,它不会自动结束,还要向下继续,但是后面没有元素了,于是就报一个称之为StopIteration的信息(名叫:停止迭代,这分明是警告)。

还要关注迭代器对象的另外一个特点,当对象lst_iter被迭代结束,即每个元素都读取了一遍之后,指针就移动到了最后一个元素的后面。如果再访问,指针并没有自动返回到首位置,而是仍然停留在末位置,所以报StopIteration,想要再开始,需要重新载入迭代对象。所以,当我在上面重新进行迭代对象赋值之后,又可以继续了。

文件迭代器

现在有一个文件,名称:208.txt,其内容如下:

Learn python with qiwsir.
There is free python course.
The website is:
http://qiwsir.github.io
Its language is Chinese.

用迭代器来操作这个文件,我们在前面讲述文件有关知识的时候已经做过了,无非就是:

>>> f = open("208.txt")
>>> f.readline()        #读第一行
'Learn python with qiwsir.\n'
>>> f.readline()        #读第二行
'There is free python course.\n'
>>> f.readline()        #读第三行
'The website is:\n'
>>> f.readline()        #读第四行
'http://qiwsir.github.io\n'
>>> f.readline()        #读第五行,也就是这真在读完最后一行之后,到了此行的后面
'Its language is Chinese.\n'
>>> f.readline()        #无内容了,但是不报错,返回空。
''

以上演示的是用readline()一行一行地读。当然,在实际操作中,我们是绝对不能这样做的,一定要让它自动进行,比较常用的方法是:

>>> for line in f:     #这个操作是紧接着上面的操作进行的
...     print line,    #Python 3: print(line)
...                            #没有打印出什么东西

这段代码之所没有打印出东西来,是因为经过前面的操作,指针已经移到了最后了。这就是迭代的一个特点,要小心指针的位置。

>>> f = open("208.txt")     #从头再来
>>> for line in f:
...     print line,    #Python 3: print(line,end='')
... 
Learn python with qiwsir.
There is free python course.
The website is:
http://qiwsir.github.io
Its language is Chinese.

上面过程用next()或者__next__()也能够读取。

>>> f = open("208.txt")
>>> f.next()          #Python 3: f.__next__()
'Learn python with qiwsir.\n'
>>> f.next()
'There is free python course.\n'
>>> f.next()
'The website is:\n'
>>> f.next()
'http://qiwsir.github.io\n'
>>> f.next()
'Its language is Chinese.\n'
>>> f.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

如果用next()或者__next__(),就可以直接读取每行的内容。这说明文件是天然的可迭代对象,不需要用iter()转换了。

再有,我们用for来实现迭代,在本质上,就是自动调用next()或者__next__(),只不过这个工作,已经让for偷偷地替我们干了,到这里,列位是不是应该给for取另外一个名字:它叫雷锋。

其实,迭代器远远不止上述这么简单,下面我们随便列举一些,在Python中还可以这样得到迭代对象中的元素。

>>> list(open('208.txt'))
['Learn python with qiwsir.\n', 'There is free python course.\n', 'The website is:\n', 'http://qiwsir.github.io\n', 'Its language is Chinese.\n']

>>> tuple(open('208.txt'))
('Learn python with qiwsir.\n', 'There is free python course.\n', 'The website is:\n', 'http://qiwsir.github.io\n', 'Its language is Chinese.\n')

>>> "$$$".join(open('208.txt'))
'Learn python with qiwsir.\n$$$There is free python course.\n$$$The website is:\n$$$http://qiwsir.github.io\n$$$Its language is Chinese.\n'

>>> a,b,c,d,e = open("208.txt")
>>> a
'Learn python with qiwsir.\n'
>>> b
'There is free python course.\n'
>>> c
'The website is:\n'
>>> d
'http://qiwsir.github.io\n'
>>> e
'Its language is Chinese.\n'

上述方式,在编程实践中不一定用得上,只是向读者展示一下,并且要明白,可以这么做,不是非要这么做。

最后透露,字典和元组都可以写成类似列表解析式的样式,也可以迭代,读者不妨摸索一下。


总目录   |   上节:文件(2)   |   下节:练习

如果你认为有必要打赏我,请通过支付宝:[email protected],不胜感激。

results matching ""

    No results matching ""