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从数据中分离出来,分别定义一个iterableiterator如下:

 

 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等常用模块与包

Python学习——02-Python基础——8-面向对象的程序设计——封装绑定方法与非绑定方

02.Python基础