python迭代器和生成器

迭代器(Iterator)

迭代的概念

通过for循环来逐个获取可迭代对象的每一项的过程就称为迭代(Iteration)。

#简单的迭代:
for i in [1,2,3]:
    print(i)
#1
#2
#3
可迭代对象

可以直接作用于for循环的对象统称为可迭代对象(Iterable)。
有以下几种:

  • 集合数据类型,如list、tuple、dict、set、str等;
  • generator,包括生成器和带yield的generator function。

我们可以使用isinstance()判断一个对象是否是Iterable对象:

from collections import Iterable

print(isinstance([1,2], Iterable))                  #True
print(isinstance((1,2), Iterable))                  #True
print(isinstance({1:2,2:4}, Iterable))              #True
print(isinstance("hello", Iterable))                #True
print(isinstance(9999, Iterable))                   #False
print(isinstance((x for x in range(5)), Iterable))  #True
迭代器

可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。

Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

注意:list、dict、str等可迭代对象(Iterable)需要使用iter()函数来变成迭代器(Iterator),而所有的生成器都是迭代器(Iterator)


生成器(Generator)

通过上面的介绍,我们知道生成器(Generator)就是迭代器(Iterator)的一种,因此它同样具有惰性,即生成器是在循环中不断地生成新的元素,而不是一次性将所有的元素全部生成再等待被循环。

生成器的这种惰性,在某种情况下能给我们带来极大的便利。你可以试想一下,如果我们通过列表生成式创建一个包含1万个元素的列表,这将会一次性占用内存中存放这足足1万个元素的空间。如果我们通过生成器来创建呢,最开始我们得到的是只是一个生成器对象,接下来使用next()函数调用它一次,它才会生成下一个元素,而且占用的内存始终为1个元素的空间。

生成器中保存的不是要生成的所有值,而是算法,是生成元素的规则。每次被next()函数调用,生成器就计算出下一个元素的值,直到抛出StopIteration的错误,表示无法继续返回下一个值了。

下面生成容量为6的列表用于验证:

L = [x * x for x in range(6)] #创建一个列表对象
print(L)  #[0, 1, 4, 9, 16, 25]

G = (x * x for x in range(6)) #创建一个生成器对象,只需把生成式的[]改成()
print(G)  #<generator object <genexpr> at 0x000F544409E8>
print(next(G))  # 0
print(next(G))  # 1
print(next(G))  # 4
print(next(G))  # 9
print(next(G))  # 16
print(next(G))  # 25
print(next(G))  # 报错:StopIteration  

我们可以通过for循环来迭代生成器,而且无需处理StopIteration的错误:

for i in (x * x for x in range(6)):
    print(i)
# 0
# 1
# 4
# 9
# 16
# 25

现在我们知道,要创建一个生成器,只需要把一个列表生成式的[]改成()即可。

但是生成器给你的惊喜远不止如此,我们可以在一个函数中包含yield关键字,那么这个函数就不在是个普通函数,而是一个更强大的生成器。

下面是实现了一个普通的函数,可以输出斐波拉契数列的前n项:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)   #输出当前项
        a, b = b, a + b
        n = n + 1
    return 'done'

fib(4)
#1
#1
#2
#3
#done

将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 'done'

f = fib(4)  
print(f)  #<generator object fib at 0x0007E59678>
for i in f:  
    print(i)
#1
#1
#2
#3

神奇的yield:

要理解yield你必须先理解当你第一次调用函数的时候,函数里的代码并没有运行,函数返回的仅仅是一个生成器。

生成器和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成了生成器之后,在每次调用next()的时候才会执行一次并且遇到yield语句返回,再次执行时直接从上次返回的yield语句处继续执行。

下面有个例子可以帮助理解:

#实现一个可以返回生成器的函数
def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)
    #运行到此处的时候,生成器就被认为变成了空

f = odd()
print(next(f))
# step 1
# 1
print(next(f))  
# step 2
# 3
print(next(f))  
# step 3
# 5
print(next(f))  
# 由于生成器下一项为空了,所以报错 :StopIteration

可以看到,每次调用该生成器都是从上次调用结束的地方开始执行。


参考:廖雪峰python教程stackoverflow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值