迭代器(iterator)
iterator: 迭代器对象
,也属于python的名词,只能迭代一次。需要满足如下的迭代器协议
-
定义了
__iter__
方法,但是必须返回自身 -
定义了
next
方法,在python3.x是__next__
。用来返回下一个值,并且当没有数据了,抛出StopIteration
-
可以保持当前的状态
自定义iterator 与数据分离
说到这里,迭代器对象基本出来了。下面大致说一下,如何让自定义的类的对象成为迭代器对象,其实就是定义__iter__
和next
方法:
In [1]: %paste
class DataIter(object):
def __init__(self, *args):
self.data = list(args)
self.ind = 0
def __iter__(self): #返回自身
return self
def next(self): # 返回数据
if self.ind == len(self.data):
raise StopIteration
else:
data = self.data[self.ind]
self.ind += 1
return data
## -- End pasted text --
In [9]: d = DataIter(1,2)
In [10]: for x in d: # 开始迭代
....: print x
....:
1
2
In [13]: d.next() # 只能迭代一次,再次使用则会抛异常
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
----> 1 d.next()
<ipython-input-1-c44abc1904d8> in next(self)
10 def next(self):
11 if self.ind == len(self.data):
---> 12 raise StopIteration
13 else:
14 data = self.data[self.ind]
从next
函数中只能向前取数据,一次取一个可以看出来,不过不能重复取数据
生成器(generator)
首先需要明确的就是生成器也是
iterator
迭代器,因为它遵循了迭代器协议.
两种创建方式
包含yield
的函数
生成器函数跟普通函数只有一点不一样,就是把 return
换成yield
,其中yield
是一个语法糖,内部实现了迭代器协议,同时保持状态可以挂起。如下:
记住一点,yield
是数据的生产者,而诸如for
等是数据的消费者。
def gen():
print 'begin: generator'
i = 0
while True:
print 'before return ', i
yield i
i += 1
print 'after return ', i
a = gen()
In [10]: a #只是返回一个对象
Out[10]: <generator object gen at 0x7f40c33adfa0>
In [11]: a.next() #开始执行
begin: generator
before return 0
Out[11]: 0
In [12]: a.next()
after return 1
before return 1
Out[12]: 1
首先看到while True
不必惊慌,它只会一个一个的执行~
看结果可以看出一点东西:
-
调用
gen()
并没有真实执行函数,而是只是返回了一个生成器对象 -
执行第一次
a.next()
时,才真正执行函数,执行到yield
一个返回值,然后就会挂起,保持当前的名字空间等状态。然后等待下一次的调用,从yield
的下一行继续执行。
还有一种情况也会执行生成器函数,就是当检索生成器的元素时,如list(generator)
, 说白了就是当需要数据的时候,才会执行。
In [15]: def func():
....: print 'begin'
....: for i in range(4):
....: yield i
In [16]: a = func()
In [17]: list(a) #检索数据,开始执行
begin
Out[17]: [0, 1, 2, 3]
yield
还有其他高级应用,后面再慢慢学习。
生成器表达式
列表生成器十分方便:如下,求10以内的奇数:
[i for i in range(10) if i % 2]
同样在python 2.4
也引入了生成器表达式
,而且形式非常类似,就是把[]
换成了()
.
In [18]: a = ( i for i in range(4))
In [19]: a
Out[19]: <generator object <genexpr> at 0x7f40c2cfe410>
In [20]: a.next()
Out[20]: 0
可以看出生成器表达式创建了一个生成器,而且生有个特点就是惰性计算
, 只有在被检索时候,才会被赋值。
小结
概括
主要介绍了大概这样几点:
-
迭代器协议
-
自定义可迭代对象与迭代器分离,保证数据复用
-
-
生成器: 特殊的迭代器,内部实现了迭代器协议