迭代器与生成器

生成器

在讲生成器之前,有必要先提一下列表生成式,列表生成式之前已经提过,它也叫列表解析式,是一种生成列表的高级用法,再来回顾一下:

给出列表 list = [1,2,3,4,5] ,生成它的每一个元素都平方后的列表:
怎么实现呢?如果用循环,那就又要折腾好几行代码了,而如果用列表解析式,一行搞定:

listx = [v**2 for v in list]
print(listx) #[1, 4, 9, 16, 25]

如上,通过列表生成式,我们可以创建一个列表,但是受到内存限制,列表容量肯定是有限的,或者我们创建了一个很大的列表,却只需要用到前面几个数据,那么很多空间就都被浪费了;

于是就有人琢磨,如果列表中的元素可以被某种算法推导出来,那么不就可以边推导边生成,只需要利用一个循环在得到我们想要的数据后就停止生成,那么不就可以节省大量空间了?

所以就有了生成器
在Python中,这种一边循环一边计算的机制,称为生成器(generator);

创建一个生成器

方法一:
第一种创建生成器的方法很简单,只需要把列表生成式的[]、或者{}改为()即可,这对列表或者字典都适用;

list = [1,2,3,4,5]
gtor = (v**2 for v in list)
print(type(gtor)) #<class 'generator'>

列表和用列表创建的生成器有一点不同就在于生成器已经不能用print直接输出全部了,因为它只是一个生成器,所以当你用print输出生成器,会输出:

list = [1,2,3,4,5]
gtor = (v**2 for v in list)
print(gtor) #<generator object <genexpr> at 0x03D85870>

如果你要遍历生成器内的元素,可以用next()函数
next()函数的作用是返回generator的下一个元素,如果没有下一个元素,抛出 StopIteration 的错误;
注意next()函数的指向从第一个元素开始,不会后退,只会向前;
但这是不推荐的,事实上直接用for循环遍历就好,next()会显得有些臃肿;

方法二:
如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator,调用函数就是生成一个generator对象
同样上例子:
先编写一个函数求斐波那契数列

def fib(max):
    n,a,b = 0,0,1
    while n<max:
        print(b,end="")
        print(' ',end="")
        a,b = b,a+b
        n += 1
fib(5) #1 1 2 3 5 

按照方法二所述,怎么把这个函数变成一个生成器呢?这里只要把print(b)的语句改为yield b就可以了;

def fib(max):
    n,a,b = 0,0,1
    while n<max:
        yield b
        print(' ',end="")
        a,b = b,a+b
        n += 1
    return 'done' #这里加一个返回值

这样函数fib()就成了一个生成器,之后next()遍历还是for循环都随便你;

f = fib(6)
for v in range(1,7):
    print(next(f),end="") #1 1 2 3 5 8

注意这条 f = fib(6) 的语句,fib(6)生成了一个有六个元素的生成器并使f指向它,因此range最多到7,超过7就会报错;
或者循环更方便:

for v in fib(6):
    print(v,end="") #1 1 2 3 5 8 

这里要理解yield的机制:普通函数是顺序执行,遇到return或者执行到做后一条语句就返回,然而变成generator的函数,在遇到yield语句时就会返回,在本例中是yield b ,所以每次都返回b,当再次执行时再从上次中断的地方继续执行;

另外还有一个问题,怎么输出函数的返回值呢?即本例中的 ‘done’,可以参照下面例子:

f = fib(7)
while True:
    try:
        print(next(f),end="")
    except StopIteration as s:
        print(s.value,end="") #s.value是捕获到StopIteration错误时得到的返回值
        break

关于yield的更详细说明有一篇技术大牛写的文章个人感觉贼拉好,一并附上:
https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/

迭代器

Python中,迭代器是一个很强大的功能,它是一个可以记住遍历位置的对象,迭代器只作用于可迭代对象,那么什么是可迭代对象呢?
可迭代对象在Python中叫做Iterable,它是一种数据类型,我们可以通过isinstance()函数来判断一个对象是否是可迭代对象:

>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('',Iterable)
True
>>> isinstance(666,Iterable)
False

可以看出,列表、字典、字符串等都是可迭代对象,而数值型不是;
要注意的是generator也是一类特殊的可迭代对象
可迭代对象都有一个特点,就是可以直接作用于for循环,也就是说它们都可以使用类似列表解析式的构建方法;

而可以被next()函数调用并不断返回下一个值得对象成为迭代器(Iterator),迭代器有两个基本的方法:iter()和next()

<注意>这里要格外区分清楚IteratorIterable,一个是迭代器,一个是可迭代对象;其中所有generator对象都是Iterator对象,而像列表list、字典dict等虽然是Iterable,但不是Iterator;

把list、dict、str等Iterable变成Iterator需要用到iter()函数:

>>> isinstance(iter([]), Iterator)
True

部分内容(不限于本文)参考自:

菜鸟编程
廖雪峰的官方网站
c语言中文网
以及《Python编程-从入门到实践》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值