迭代器、生成器、生成器表达式

本质上,迭代器、生成器和生成器表达式是同一种东西,向Python解释器不断的提供对象。

只不过为了达到同样的目的,迭代器、生成器和生成器表达式实现的方式有所不同。

迭代器

使用类去实现Python的迭代器协议,重写__iter__和__next__魔术方法。

其中__iter__函数必须返回一个实现了__next__函数的对象,__next__函数的返回值就是迭代器对象每次提供的对象内容。

如果在迭代过程中,__next__函数抛出了StopIteration异常,则迭代器中止。

'''生成N个1~10之间随机数字的迭代器
'''
import random

class MyIter:
    def __init__(self,n):
        self.count = 0
        self.ntimes = n

    def __iter__(self):
    '''必须返回一个实现了__next__的对象'''
        return self

    def __next__(self):
    '''函数返回值就是每次迭代时由迭代器提供的对象,
       函数抛出StopIteration时,意味着迭代器的中止
    '''
        if self.count >= self.ntimes:
            raise StopIteration
        else:
            self.count += 1
            return random.randint(1,10)

手动迭代:

myiter = MyIter(5)
itr = iter(myiter) # 内置函数iter会调用myiter.__iter__()
print(next(itr))   # 内置函数next会调用itr.__next__()
print(next(itr))
print(next(itr))
print(next(itr))
print(next(itr))
print(next(itr)) # 第6次调用itr的__next__函数,产生StopIteration异常

也可以使用for循环来从迭代器取内容:

for i in MyIter(5):
    print(i)

屏幕上会打印5个1~10之间的随机数字,并且for循环会处理StopIteration异常,中止继续迭代。

为了提供几个数字,迭代器却需要从类开始构建,并实现迭代器协议,有些过于繁琐。于是就有了生成器。

生成器

生成器是一个函数。与一般函数不同的是,生成器函数多了一个“返回”关键字yield,通过yield“返回”的内容是迭代过程中生成器提供给Python解释器的对象。一旦生成器函数由return返回了,就意味着函数的结束也意味着生成器中止。

'''生成N个1~10之间随机数字的生成器
'''
import random

def mygen(n):
    for i in range(n):
        yield random.randint(1, 10)

 mygen是一个生成器函数,通过yield“返回”内容时控制流从函数暂时交还给函数调用处。只要迭代继续进行,控制流就能回到函数中上一次yield返回处继续执行。当n次后,循环语句结束,mygen函数通过return返回None。mygen函数一旦执行了return,就意味着将控制流彻底的从函数中交还给了函数调用处。至此,产生StopIteration异常,生成器中止。

手动迭代:

gen = mygen(5)    #获得一个生成器
print(next(gen))   # 打印yield的“返回值”
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
# 第6次调用时,for循环结束
# mygen函数通过return语句返回None
# 函数结束,生成器中止,产生StopIteration异常
print(next(gen)) 

也可以使用for循环来从迭代器取内容:

for i in mygen(5):
    print(i)

屏幕上会打印5个1~10之间的随机数字,for循环会处理StopIteration异常,中止继续迭代。

实现同样功能时,生成器仅仅需要3行代码就可以做到,可以把生成器函数看做迭代器的语法糖。

生成器表达式

就像列表表达式(也称为列表推导式)是生成列表的简化方式,生成器表达式(也称为生成器推导式)是生成器函数的简化方式。

'''生成5个1~10之间随机数的生成器表达式
'''
gen = (random.randint(1,10) for i in range(5)) 

表达式的结果是一个生成器对象,通过gen可以手动或for迭代。

这一次代码简化到了一行,但是与生成器函数相比有一些不便的地方在于:

1. 显然生成随机数的个数不能通过参数传入,只能写成固定的数值5

2.再想获得一个生成器迭代时,只能把表达式重新再写一遍,不太符合代码复用的原则。

gen = (random.randint(1, 10) for _i in range(5))
for i in gen:
    print(i)

# 再想使用时,只能把表达式重新再写一遍
gen2 = (random.randint(1, 10) for _i in range(5))
print(next(gen2))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章