欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

迭代器和生成器总结

程序员文章站 2022-06-30 11:56:26
迭代器: 是一个抽象的概念,任何对象,如果它的类有 next 方法和 iter 方法返回自己本身,对于 string、list、dict、tuple 等这类容器对象,使用 for 循环遍历是很方便的。在后台 for 语句对容器对象调用 iter()函数,iter()是 python 的内置函数。it ......

迭代器:


是一个抽象的概念,任何对象,如果它的类有 next 方法和 iter 方法返回自己本身,对于 string、list、dict、tuple 等这类容器对象,使用 for 循环遍历是很方便的。在后台 for 语句对容器对象调用 iter()函数,iter()是 python 的内置函数。iter()会返回一个定义了 next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是 python 的内置函数。在没有后续元素时,next()会抛出一个 StopIteration 异常。


关于iter()和next():


1、iter(iterable)函数是把可迭代对象的迭代器取出来,内部是调用可迭代对象的__iter__方法,来取得迭代器的
2、next(iterator)函数是通过迭代器取得下一个位置的值,内部是调用迭代器对象的__next__方法,来取得下一个位置的值
注意: 
当我们已经迭代完最后一个数据之后,再次调用next()函数会抛出StopIteration的异常,来告诉我们所有数据都已迭代完成,不用再执行next()函数了。
所以:
我们要想构造一个迭代器,就要实现它的__next__方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可。
结论: 
一个实现了__iter__方法和__next__方法的对象,就是迭代器,迭代器同时也是一个可迭代对象


示例代码:

class FibIterator(object):
    """斐波那契数列迭代器"""

    def __init__(self, n):
        # 记录生成fibonacci的数列的个数
        self.n = n
        # 记录当前遍历的下标
        self.current_index = 0
        # 记录fibonacci数列前面的两个值
        self.num1 = 0
        self.num2 = 1

    def __next__(self):
        """被next()函数调用来获取下一个数"""
        if self.current_index < self.n:
            num = self.num1
            self.num1, self.num2 = self.num2, self.num1 + self.num2
            self.current_index += 1
            return num
        else:
            raise StopIteration

    def __iter__(self):
        """迭代器的__iter__返回自身即可"""
        return self


if __name__ == '__main__':
    fib = FibIterator(10)
    for num in fib:
        print("  ", num, end="")

# 运行结果: 0 1 1 2 3 5 8 13 21 34

 

生成器(Generator):


是创建迭代器的简单而强大的工具。生成器是一种特殊的迭代器,它比迭代器写起来更加优雅,它们写起来就像是正规的函数,只是在需要返回数据的时候使用 yield 语句。每次 next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)

 

最简单的生成器就是把一个列表生成式的[ ]改成( )
例如:g = (x*2 for x in rang(5)),就是一个生成器
另外一种生成器的创建方法就是这一个函数里面加yield:
例如把上面的斐波那契函数改写成生成式如下:

def fib(n):
    current_index = 0
    num1, num2 = 0, 1
    while current_index < n:
        # print(num1) # 打印斐波那契数列
        """
         1. 假如函数中有yield,则不再是函数,而是生成器
         2. yield 会产生一个断点
         3. 假如yield后面紧接着一个数据,就会把数据返回,
            作为next()函数或者for ...in...迭代出的下一个值
        """
        yield num1
        num1, num2 = num2, num1 + num2
        current_index += 1

if __name__ == '__main__':
    # 假如函数中有yield,则不再是函数,而是一个生成器
    gen = fib(10)

    #  生成器是一种特殊的迭代器
    for num in gen:
        print(num)

# 运行结果: 0 1 1 2 3 5 8 13 21 34

通过代码可以很直观看出生成器更优雅更省代码

yield其实就是保存当前程序执行状态。你用 for 循环的时候,每次取一个元素的时候就会计算一次。用 yield 的函数叫 generator,和 iterator 一样,它的好处是不用一次计算所有元素,而是用一次算一次,可以节省很多空间。generator每次计算需要上一次计算结果,所以用 yield,否则一 return,上次计算结果就没了,简单来说一个函数里面加了yield就是生成器。
还有我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

总结:


1、使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
2、yield关键字有两点作用:
 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
3、可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)

 

区别:


生成器能做到迭代器能做的所有事,而且因为自动创建了 iter()和 next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出 StopIteration 异常。