Python的迭代器(iterator)和生成器(constructor)

一、迭代器(iterator)

1.迭代器的概述

在Python中,for循环可以用于Python中的任何类型,包括列表、元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器

迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发 StopIteration。任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且 捕捉StopIteration异常来确定何时离开。

迭代器的另一个优点就是它不要求你事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不 存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值 (Lazy evaluation)。

迭代器更大的功劳是提供了一个统一的访问集合的接口。只要是实现了__iter__()方法的对象,就可以使用迭代器进行访问。

2.迭代器的使用

使用内建的工厂函数iter(iterable)可以获取迭代器对象:注意:迭代器是对象

1 lst = range(2)
2 it = iter(lst)
3 print  it
4 #输出结果
<listiterator object at 0x00BB62F0>

 使用迭代器的next()方法可以访问下一个元素:

1 print it.next()
2 0

如何判断迭代器还有更多的元素可以访问呢?Python里的迭代器并没有提供类似has_next()这样的方法。
那么在这个例子中,我们如果访问到了最后一个元素1后,再使用next()方法会怎样呢?

it.next() 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
StopIteration

 Python遇到这样的情况时将会抛出StopIteration异常。事实上,Python正是根据是否检查到这个异常来决定是否停止迭代的。
这种做法与迭代前手动检查是否越界相比各有优点。但Python的做法总有一些利用异常进行流程控制的嫌疑。

看到这个地方大家应该明白了迭代器的原理了吧,现在来说一说前面所说到的列表,元祖等可以迭代的原理了,还是要归功于自带的__iter__(前面和后面加上‘__’就是内置方法哦)

实例:

1 list1 = range(5) #生成一个list
2 print '*****list1=', list1 #打印list1内容
3 print '*****type=', type(list1) #打印list类型
4 it = iter(list1) #讲list1变成迭代器
5 print '*****type(iter)=', type(it) #打印生成后的对象类型
6 print '*****it=', it #打印it的信息
7 print '*****dir(list1)=', dir(list1) #查看列表方法

 运行结果:

首先python对关键字in后的对象调用iter函数迭代器,然后调用迭代器的next方法获得元素,直到抛出StopIteration异常。

因为迭代器如此普遍,python专门为for关键字做了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。

二、生成器(Generator)

1. yield解析

yield 关键字用来定义生成器(Generator),其具体功能是可以当return使用,从函数里返回一个值,不同之处是用yield返回之后,可以让函数从 上回yield返回的地点继续执行。也就是说,yield返回函数,交给调用者一个返回值,然后再“瞬移”回去,让函数继续运行, 直到下一跳yield语句再返回一个新的值。

使用yield返回后,调用者实际得到的是一个迭代器对象,迭代器的值就是返回值,而调用该迭代器的next()方法会导致该函数恢复yield语句的执行环境继续往下跑,直到遇到下一个yield为止,如果遇不到yield,就会抛出异常表示迭代结束。

看一个例子:

def test_yield():
    yield 1
    yield 2
    yield (1,2)

test = test_yield()
print test.next()
print test.next()
print test.next()

运行结果:

2.yield的使用原理

1. 包含yield的函数

假如你看到某个函数包含了yield,这意味着这个函数已经是一个Generator,它的执行会和其他普通的函数有很多不同。比如下面的简单的函数:

1 def h():  
2     print 'To be'
3     yield 5  
4 h() 

可以看到,调用h()之后,print 语句并没有执行!这就是yield,那么,如何让print 语句执行呢?这就是后面要讨论的问题,通过后面的讨论和学习,就会明白yield的工作原理了。

参考文章:http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html

参考文章:http://blog.163.com/l_greatsea/blog/static/2049860442013220113640476/

posted @ 2016-04-13 21:02  楚时邀月  阅读(506)  评论(0编辑  收藏  举报