这一讲的主要目的是为了大家在读 Python 程式的时候对回圈物件有一个基本概念。
回圈物件的并不是随著 Python 的诞生就存在的,但它的发展迅速,特别是 Python 3x 的时代,回圈物件正在成为回圈的标准形式。
什么是回圈物件
回圈物件是这样一个物件,它包含有一个 next() 方法 (__next__() 方法,在 python 3x 中), 这个方法的目的是进行到下一个结果,而在结束一系列结果之后,举出 StopIteration 错误。
当一个回圈结构(比如 for)呼叫回圈物件时,它就会每次回圈的时候呼叫 next() 方法,直到 StopIteration 出现,for 回圈接收到,就知道回圈已经结束,停止呼叫 next() 。
假设我们有一个 test.txt 的档案:
1234
abcd
efg
我们执行一下 python 命令列:
>>>f = open(‘test.txt’)
>>>f.next()
>>>f.next()
…
不断输入 f.next(),直到最后出现 StopIteration
open() 返回的实际上是一个回圈物件,包含有 next() 方法。而该 next() 方法每次返回的就是新的一行的内容,到达档案结尾时举出 StopIteration 。这样,我们相当于手工进行了回圈。
自动进行的话,就是:
for line in open(‘test.txt’):
print line
在这里,for 结构自动呼叫 next() 方法,将该方法的返回值赋予给 line 。回圈知道出现 StopIteration 的时候结束。
相对于序列,用回圈物件的好处在于:不用在回圈还没有开始的时候,就生成好要使用的元素。所使用的元素可以在回圈过程中逐次生成。这样,节省了空间,提高了效率,程式设计更灵活。
迭代器
从技术上来说,回圈物件和 for 回圈呼叫之间还有一个中间层,就是要将回圈物件转换成迭代器 (iterator) 。这一转换是通过使用 iter() 函式实现的。但从逻辑层面上,常常可以忽略这一层,所以回圈物件和迭代器常常相互指代对方。
生成器
生成器 (generator) 的主要目的是构成一个使用者自定义的回圈物件。
生成器的编写方法和函式定义类似,只是在 return 的地方改为 yield 。生成器中可以有多个 yield 。当生成器遇到一个 yield 时,会暂停执行生成器,返回 yield 后面的值。当再次呼叫生成器的时候,会从刚才暂停的地方继续执行,直到下一个 yield 。生成器自身又构成一个回圈器,每次回圈使用一个 yield 返回的值。
下面是一个生成器:
def gen():
a = 100
yield a
a = a*8
yield a
yield 1000
该生成器共有三个 yield, 如果用作回圈器时,会进行三次回圈。
for i in gen():
print i
再考虑如下一个生成器:
def gen():
for i in range(4):
yield i
它又可以写成生成器表示式 (Generator Expression):
G = (x for x in range(4))
生成器表示式是生成器的一种简便的编写方式。读者可进一步查阅。
表推导
表推导 (list comprehension) 是快速生成表的方法。它的语法简单,很有实用价值。
假设我们生成表 L:
L = []
for x in range(10):
L.append(x**2)
以上产生了表 L,但实际上有快捷的写法,也就是表推导的方式:
L = [x**2 for x in range(10)]
这与生成器表示式类似,只不过用的是中括号。
(表推导的机制实际上是利用回圈物件,有兴趣可以查阅。)
练习 下面的表推导会生成什么?
xl = [1,3,5]
yl = [9,12,13]
L = [ x**2 for (x,y) in zip(xl,yl) if y > 10]
总结
回圈物件
生成器
表推导