###
这里就涉及到很重要的一个概念-----可迭代对象,除此之外还有一个与它很类似的概念,叫做迭代对象,很多人经常分不清楚他们。迭代对象是指实现了__iter__
与next
方法的对象,而可迭代对象可以只实现__iter__
方法,也可以两个都实现。有的可迭代对象的迭代对象就是它本身。说了那么多,不如我们直接自己实现一下:
class MyRange(object):
def __init__(self, n):
self.idx = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.idx < self.n:
val = self.idx
self.idx += 1
return val
else:
raise StopIteration()
上面这个类既实现了__iter__
方法,也实现了next方法,并且它的__iter__
方法返回了它自身,所以这个可迭代对象的的迭代对象就是它自身。上面的类其实就相当于我们python中使用的xrange函数。
myRange = MyRange(3)
for i in myRange:
print i
如果一个可迭代对象同时它的迭代对象又是它本身的话会导致一个问题,无法重复迭代,例如:
从图中可以看到,当我迭代完一次过后,迭代器就被迭代完了,当我第二次用for迭代的时候没有任何输出。解决这一问题的办法就是分离迭代对象与可迭代对象。
class Zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
return ZrangeIterator(self.n)
class ZrangeIterator:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
zrange = Zrange(3)
print zrange is iter(zrange)
print [i for i in zrange]
print [i for i in zrange]
注:我们可以通过iter()内建函数获取一个可迭代对象的迭代对象,然后通过is比较,例如:
可以看到列表类型的迭代对象不是它本身
####手动迭代:iter与next
相信通过上面的例子,大家已经很清楚迭代器的工作原理了,下面我们来通过手动迭代,更加直观的来认识迭代器。
查看大图可以看到,我们创建了一个a列表,这个列表中实现了__iter__
方法,我们已经知道这个方法返回了一个迭代对象,我们接着看一下
可以看到这个迭代对象中实现了next方法。
然后我们通过手动迭代观察了一下,每调用一次next迭代器就走一步,走到头就抛出StopIteration。我们的for循环等迭代操作也就是利用了这个原理。
注:文件也是一个可迭代对象,我们可以用for循环按行遍历
####列表解析初探
我们用一个例子走近列表解析的大门,加入我们想要修改一个列表,以前我们能会这么做:
L = [1,2,3,4,5]
for i in range(len(L)):
L[i] += 10
print L
但是实际上这样太麻烦了,我们完全可以偷懒,而且还可以提高运行效率(通常列表解析效率更高):
L = [x+10 for x in L]
print L
现在让我们来更详细的剖析一下这个例子:
列表解析写在一个方括号中,因为他们最终是构建一个新的列表(python3.0中,可以用解析构造元组,字典等)。上面的列表解析式执行的操作就是,依次从原来的L列表中取出一个数,暂存在x变量里,然后这个x在加上10,直到遍历完整个L表,同时也就形成了一个新的列表。
再来看一个例子:
a = ['123\n','234\n']
b = [x.strip() for x in a]
print b
我们对原来列表中的每一项去掉了换行符,然后组成了新的列表
####生成器
刚刚我们也了解了列表解析式是什么东西,其实生成器的写法与列表解析式差不多,只是把方括号改为圆括号。例如:
a = (x for x in range(11))
这就构造了一个生成器。
那到底生成器是什么东西?它有什么用呢?
我们通常在写列表解析式的时候,都是表达式执行过后都会直接生成一个序列,比如:
a = [x for x in range(11)]
就会生成
[1,2,3,4,5,6,7,8,9,10]
这样一个完整的序列,但是生成器表达式执行后生成的不是一个序列,而是相当于一种算法,每运行一次这个表达式都会返回序列的下一个值,这个值是现场生成的(不是一开始就保存在内存中的)。这样有什么好处,相信大家都可以想到——节约空间(也许小程序中感受不到有什么差异,但是当这个序列很大的时候就…)
实际上,生成器生成的是一个迭代对象也是一个可迭代对象。所以,其实它也有一个next方法:
当然,这种写法构造的生成器,实际上它能完成的功能不丰富,就像一个列表解析式一样。所以,还有另外一种写法,例如:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
上面的函数执行后会打印一串斐波那契数列。我们同样可以写成一个生成器,这只需要简单的把print改为yield。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
有了yield之后,每调用一次next方法,就会返回yield后面的值,并且不再往后执行,而是在下一次调用next方法时执行,一直重复,直到抛出StopIteration错误。
####send与close方法
这两个方法也是与生成器相关的,但是感觉用的不太多,就不详细介绍了。