Python的迭代器与生成器

Python中的生成器和迭代器方便好用,可是平时对生成器和迭代器的特性掌握的不是很到位,今天将这方面的知识整理一下。python

迭代器

为了更好的理解迭代器和生成,咱们须要简单的回顾一下迭代器协议的概念。算法

迭代器协议 

1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引发一个StopIteration异常,以终止迭代 (只能日后走不能往前退)编程

2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)函数

3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。工具

for循环

for循环的本质:循环全部对象,全都是使用迭代器协议。spa

for循环就是基于迭代器协议提供了一个统一的能够遍历全部对象的方法,即在遍历以前,先调用对象的__iter__方法将其转换成一个迭代器,而后使用迭代器协议去实现循环访问,这样全部的对象就均可以经过for循环来遍历了,code

列表,字符串,元组,字典,集合,文件对象等本质上来讲都不是可迭代对象,在使用for循环的时候内部是先调用他们内部的_iter_方法,使他们变成了可迭代对象,而后在使用可迭代对象的_next_方法依次循环元素,当元素循环完时,会触发StopIteration异常,for循环会捕捉到这种异常,终止迭代。对象

如访问一个list,能够使用平时习惯的写法:blog

#for循环访问
#for循环l本质就是遵循迭代器协议的访问方式,先调用diedai_l=l.__iter__()方法,或者直接diedai_l=iter(l),而后依次执行diedai_l.next(),直到for循环捕捉到StopIteration终止循环
li = [1,2,3,4]
for i in li:#li_iter = li._iter_()
    print(i)#li_iter._next_

也能够直接使用迭代器访问:内存

#迭代器协议访问
li = [1,2,3,4]
f = li.__iter__()#第一步,先经过内部的_iter_方法,先把对象变成可迭代对象
print(f.__next__())#对可迭代对象用_next_方法取值
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())#StopIteration,超出边界会报错

生成器

在介绍生成器以前,先简单介绍一下列表生成式

列表生成式

列表生成式即List Comprehensions,是Python内置的很是简单却强大的能够用来建立list的生成式。

举个例子,要生成list[1,2,3,4,5,6,7,8,9,10]能够用range(1,11):

>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

列表生成式能够代替循环在编程中偷懒,如生成[1x1, 2x2, 3x3, ..., 10x10]怎么作?能够用普通的循环,也能够用列表生成器完成,以下:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

经过列表生成式,咱们能够直接建立一个列表。可是,受到内存限制,列表容量确定是有限的。并且,建立一个包含100万个元素的列表,不只占用很大的存储空间,若是咱们仅仅须要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

因此,若是列表元素能够按照某种算法推算出来,那咱们是否能够在循环的过程当中不断推算出后续的元素呢?这样就没必要建立完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

建立生成器的两种方法

第一种方法很简单,只要把一个列表生成式的[]改为(),就建立了一个generator:

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x104feab40>

L是一个list,而g是一个generator,若是想要访问生成器中元素,须要用生成器的next()方法。或者利用for循环,由于generator也是一个可迭代的对象。

第二种方法须要借助“yield”,以计算斐波那契数列为例,展现一个函数如何变成生成器,直接上代码:

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

函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。再举一个简单的例子,定义generator,返回1,3,5:

>>> def odd():
...     print 'step 1'
...     yield 1
...     print 'step 2'
...     yield 3
...     print 'step 3'
...     yield 5
...
>>> o = odd()
>>> o.next()
step 1
1
>>> o.next()
step 2
3
>>> o.next()
step 3
5
>>> o.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

能够看到,odd不是普通函数,而是generator,在执行过程当中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield能够执行了,因此,第4次调用next()就报错。

一样,在获取元素时,大多数时候运用for循环。

相关文章
相关标签/搜索