Iterator 迭代器 和 Generator 生成器
1. Iterators - python中的所有容器都是可以迭代的,for语句会调用容器对象中的__iter__()方法。该方法会返回一个定义了的next()方法的迭代器,在容器中逐一访问元素,当没有后续元素时候,抛出StopIteration异常。
用for loop 来对list进行操作
for i in [1,2,3,4]:
print i
1
2
3
4
用for loop来对string进行操作
for c in 'python':
print c
p
y
t
g
o
n
用for loop来对dictionary进行操作
for k in {'x':1, 'y':2}:
print k
y
x
用for loop来对文件进行操作
for line in open('a.txt'):
print line
first line
second line
称上述对象为可迭代对象。
有iter方法对可迭代对象进行操作,并且返回一个迭代器。
x = iter([1,2])
print x
<listiterator object at 0x0000000001DA29E8>
print x.next()
1
print x.next()
2
print x.next()
print x.next()
StopIteration
每次都可以使用next()方法访问下一个元素,如果不存在下一个元素,那么就会raise一个StopIteration的异常。
迭代器的实现
class yrange:
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()
>>> y = yrange(3)
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
StopIteration
>>> list(yrange(5))
[0, 1, 2, 3, 4]
>>> sum(yrange(5))
10
有些不同的是下面的例子,对于 iterable和iterator用文字理解有点描述能力不足,就看例子解释。
class zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
return zrange_iter(self.n)
class zrange_iter:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
# Iterators are iterables too.
# Adding this functions to make them so.
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
上述代码不同的是,zrange作为 iterable,zrange_iter作为iterator,相比较于yrange我们来测试cases。
>>> y = yrange(5)
>>> list(y)
[0, 1, 2, 3, 4]
>>> list(y)
[]
>>> z = zrange(5)
>>> list(z)
[0, 1, 2, 3, 4]
>>> list(z)
[0, 1, 2, 3, 4]
对于上述的结果,总结是当iterable和iterator是同一个对象的时候,这个就是一个single iteration,单次的迭代。
我觉得比较好理解的就是y和z都是同样的object,他们并没有变,变的是迭代方法里的next函数。其中的i进行了变化。对于y而言,第一次list方法操作,next已经不会再
继续取下一个元素,而对于z而言,本身就含有一个zrange_iter的返回对象,每次都会从i=0开始进行next取下一个元素。这个就是最基本的区别。
2. Generator
对于生成器generator来说,就是一个生产一系列结果而不是一个单一结果的方法。
def yrange(n):
i = 0
while i < n:
yield i
i += 1
这里要提一下yield关键词,作用类似return,返回当前的值,但是并且保存,下次访问的时候从之前的保存值开始继续。
y = yrange(3)
print y
<generator object yrange at 0x0000000001DF31B0>
print y.next()
1
print y.next()
2
print y.next()
print y.next()
StopIteration
所以可以见得,一个generator也是一个迭代器iterator。
当一个构造器方法被调用的时候,先返回一个构造器对象,但是并没有执行方法。一直到next方法被调用,才执行,并且一直遇到yield关键词的时候,返回给next方法值。
def foo():
print "begin"
for i in range(3):
print "before yield", i
yield i
print "after yield", i
print "end"
f = foo()
f.next()
# begin
# before yield 0
0
f.next()
# after yield 0
# before yield 1
1
f.next()
# after yield 1
# before yield 2
2
f.next()
# after yield 2
# end
# Traceback (most recent call last):
# StopIteration
补充例子,Fibonacci数列。
def fib(max):
a, b = 1, 1
while a < max:
yield a #generators return an iterator that returns a stream of values.
a, b = b, a+b
第一次碰到yield,返回a=1,继续a,b=1,2.
第二次,返回a=1,继续a,b=2,3
第三次,返回a=2,继续a,b=3,5
。。
最终得到Fib:1,1,2,3,5,8,13
生成器是迭代器的一种,通过生成器可以免去写迭代器的繁琐代码。迭代方法如下:
class Fib:
def __init__(self, max):
self.max = max
def __iter__(self):
self.a = 0
self.b = 1
return self
def next(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib
3. Generator Expression
Generator Expression就是generator版本的list comprehensions。看上去像是list comprehensions,但是返回的是一个generator,而不是一个list。
用list comprehensions的话
l = [x*2 for x in range(5)]
print l
# [0,2,4,6,8]
print sum(l)
# 20
而如果用的是Generator Expression,就仅仅是把中括号换成小括号。你会得到如下:
l = (x*2 for x in range(5))
print l
# <generator object <genexpr> at 0x0000000001E031B0>
print sum(l)
# 20
在这里你可以看到,l是一个generator对象,并不是一个list。所以说Generator Expression不仅减少了内存的开销,还提供了iterator的functionality,我们可以通过next方法来获得generator生成的item,比如:
print l.next()
# 0
print l.next()
# 2
但是要注意的是,Generator Expression得到的generator都仅仅是提供了一个 single iteration,单次迭代。如果在sum方法之后再调用next,就会抛出异常。