Python学习——02-Python基础——5-迭代器协议和生成器
Posted c富-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习——02-Python基础——5-迭代器协议和生成器相关的知识,希望对你有一定的参考价值。
1.迭代器(iterator)
要说生成器,必须首先说迭代器
1.区分iterable,iterator与itertion
讲到迭代器,就需要区别几个概念:iterable
,iterator
,itertion
, 看着都差不多,其实不然。下面区分一下。
-
itertion
: 就是迭代
,一个接一个(one after another),是一个通用的概念,比如一个循环遍历某个数组。 -
iterable
: 这个是可迭代对象
,属于python的名词,范围也很广,可重复迭代,满足如下其中之一的都是iterable
:-
可以
for
循环:for i in iterable
-
可以按
index
索引的对象,也就是定义了__getitem__
方法,比如list,str
; -
定义了
__iter__
方法。可以随意返回。 -
可以调用
iter(obj)
的对象,并且返回一个iterator
-
-
iterator
:迭代器对象
,也属于python的名词,只能迭代一次。需要满足如下的迭代器协议
-
定义了
__iter__
方法,但是必须返回自身 -
定义了
next
方法,在python3.x是__next__
。用来返回下一个值,并且当没有数据了,抛出StopIteration
-
可以保持当前的状态
-
1 In [3]: s = ‘hi‘ 2 3 In [4]: s.__getitem__ 4 Out[4]: <method-wrapper ‘__getitem__‘ of str object at 0x7f9457eed580> 5 6 In [5]: s.next # 没有next方法 7 --------------------------------------------------------------------------- 8 AttributeError Traceback (most recent call last) 9 <ipython-input-5-136d3c11be25> in <module>() 10 ----> 1 s.next 11 12 AttributeError: ‘str‘ object has no attribute ‘next‘ 13 14 In [6]: l = [1,2] # 同理 15 16 In [7]: l.__iter__ 17 Out[7]: <method-wrapper ‘__iter__‘ of list object at 0x7f945328c320> 18 19 In [8]: l.next 20 --------------------------------------------------------------------------- 21 AttributeError Traceback (most recent call last) 22 <ipython-input-8-c6f8fb94c4cd> in <module>() 23 ----> 1 l.next 24 25 AttributeError: ‘list‘ object has no attribute ‘next‘ 26 In [9]: iter(s) is s #iter() 没有返回本身 27 Out[9]: False 28 In [10]: iter(l) is l #同理 29 Out[10]: False
但是对于iterator
则不一样如下, 另外iterable
可以支持多次迭代,而iterator
在多次next
之后,再次调用就会抛异常,只可以迭代一次。
1 In [13]: si = iter(s) 2 3 In [14]: si 4 Out[14]: <iterator at 0x7f9453279dd0> 5 6 In [15]: si.__iter__ # 有__iter__ 7 Out[15]: <method-wrapper ‘__iter__‘ of iterator object at 0x7f9453279dd0> 8 9 In [16]: si.next #拥有next 10 Out[16]: <method-wrapper ‘next‘ of iterator object at 0x7f9453279dd0> 11 12 In [20]: si.__iter__() is si #__iter__返回自己 13 Out[20]: True
这样,由这几个例子可以解释清楚这几个概念的区别。
2.自定义iterator 与数据分离
说到这里,迭代器对象基本出来了。下面大致说一下,如何让自定义的类的对象成为迭代器对象,其实就是定义__iter__
和next
方法:
1 In [1]: %paste 2 class DataIter(object): 3 4 def __init__(self, *args): 5 self.data = list(args) 6 self.ind = 0 7 8 def __iter__(self): #返回自身 9 return self 10 11 def next(self): # 返回数据 12 if self.ind == len(self.data): 13 raise StopIteration 14 else: 15 data = self.data[self.ind] 16 self.ind += 1 17 return data 18 ## -- End pasted text -- 19 20 In [9]: d = DataIter(1,2) 21 22 In [10]: for x in d: # 开始迭代 23 ....: print x 24 ....: 25 1 26 2 27 28 In [13]: d.next() # 只能迭代一次,再次使用则会抛异常 29 --------------------------------------------------------------------------- 30 StopIteration Traceback (most recent call last) 31 ----> 1 d.next() 32 <ipython-input-1-c44abc1904d8> in next(self) 33 10 def next(self): 34 11 if self.ind == len(self.data): 35 ---> 12 raise StopIteration 36 13 else: 37 14 data = self.data[self.ind]
从next
函数中只能向前取数据,一次取一个可以看出来,不过不能重复取数据,那这个可不可以解决呢?
我们知道iterator
只能迭代一次,但是iterable
对象则没有这个限制,因此我们可以把iterator
从数据中分离出来,分别定义一个iterable
与iterator
如下:
1 class DataIterator(object): # iterator: 迭代器 2 3 def __init__(self, data): 4 self.data = data.data 5 self.ind = 0 6 7 def __iter__(self): 8 return self 9 10 def next(self): 11 if self.ind == len(self.data): 12 raise StopIteration 13 else: 14 data = self.data[self.ind] 15 self.ind += 1 16 return data 17 18 if __name__ == ‘__main__‘: 19 d = Data(1, 2, 3) 20 for x in d: 21 print x, 22 for x in d: 23 print x,
输出:
1,2,3 1,2,3
可以看出来数据可以复用,因为每次都返回一个DataIterator
,但是数据却可以这样使用,这种实现方式很常见,比如xrange
的实现便是这种数据与迭代分离的形式,但是很节省内存,如下:
1 In [8]: sys.getsizeof(range(1000000)) 2 Out[8]: 8000072 3 4 In [9]: sys.getsizeof(xrange(1000000)) 5 Out[9]: 40
另外有个小tips, 就是为什么可以使用for 迭代迭代器对象,原因就是for
替我们做了next
的活,以及接收StopIteration
的处理。
迭代器大概就记录到这里了,下面开始一个特殊的更加优雅的迭代器: 生成器
1.生成器(generator)
首先需要明确的就是生成器也是iterator
迭代器,因为它遵循了迭代器协议.
1.两种创建方式
1.包含yield
的函数
生成器函数跟普通函数只有一点不一样,就是把 return
换成yield
,其中yield
是一个语法糖,内部实现了迭代器协议,同时保持状态可以挂起。如下:
记住一点,yield
是数据的生产者,而诸如for
等是数据的消费者。
1 def gen(): 2 print ‘begin: generator‘ 3 i = 0 4 while True: 5 print ‘before return ‘, i 6 yield i 7 i += 1 8 print ‘after return ‘, i 9 10 a = gen() 11 12 In [10]: a #只是返回一个对象 13 Out[10]: <generator object gen at 0x7f40c33adfa0> 14 15 In [11]: a.next() #开始执行 16 begin: generator 17 before return 0 18 Out[11]: 0 19 20 In [12]: a.next() 21 after return 1 22 before return 1 23 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
还有其他高级应用,后面再慢慢学习。
2.生成器表达式
列表生成器十分方便:如下,求10以内的奇数:[i for i in range(10) if i % 2]
同样在python 2.4
也引入了生成器表达式
,而且形式非常类似,就是把[]
换成了()
.
1 In [18]: a = ( i for i in range(4)) 2 3 In [19]: a 4 Out[19]: <generator object <genexpr> at 0x7f40c2cfe410> 5 6 In [20]: a.next() 7 Out[20]: 0
可以看出生成器表达式创建了一个生成器,而且生有个特点就是惰性计算
, 只有在被检索时候,才会被赋值。
之前有篇文章:python 默认参数问题及一个应用,最后有一个例子:
1 def multipliers(): 2 return (lambda x : i * x for i in range(4)) #修改成生成器 3 print [m(2) for m in multipliers()]
这个就是说,只有在执行m(2)
的时候,生成器表达式里面的for
才会开始从0循环,然后接着才是i * x
,因此不存在那篇文章中的问题.
惰性计算这个特点很有用,上述就是一个应用,2gua这样说的:
个人理解就是就是可以利用生成器来作为数据管道使用,当被检索的时候,每次拿出一个数据,然后向下面传递,传到最后,再拿第二个数据,在下面的例子中会详细说明。
其实本质跟迭代器差不多,不一次性把数据都那过来,需要的时候,才拿。
基础版:http://www.runoob.com/python3/python3-iterator-generator.html
3.三元运算
三元运算是if-else 语句的快捷操作,也被称为条件运算。
1 #样式:[on_true] if [expression] else [on_false] 2 #例子: 3 x, y = 50, 25 4 small = x if x < y else y 5 #还可以嵌套使用,然当还可以更多层嵌套 6 a,b,c = 10,20,5 7 minValue = a if a < b and a < c else (b if b < a and b < c else c)
以上是关于Python学习——02-Python基础——5-迭代器协议和生成器的主要内容,如果未能解决你的问题,请参考以下文章
Python学习——02-Python基础——4-变量与递归函数
Python学习——02-Python基础——4-文件处理与三元运算
Python学习——02-Python基础——2-运算符与基本数据类型——课后作业
Python学习——02-Python基础——7-模块——time与random等常用模块与包