• 迭代器和生成器总结


    迭代器:


    是一个抽象的概念,任何对象,如果它的类有 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 异常。

  • 相关阅读:
    Codeforces Round #709 (Div. 2, based on Technocup 2021 Final Round)
    Codeforces Round #708 (Div. 2)
    Educational Codeforces Round 106 (Rated for Div. 2)
    ccf csp 202012-1
    带配额的文件系统 (带模拟)
    阔力梯的树
    Codeforces Round #707 (Div. 2, based on Moscow Open Olympiad in Informatics)
    如何获取某个网站IP地址?
    C++开发者眼中的Java关键字abstract
    Java代码中如何获文件名和行号等源码信息?
  • 原文地址:https://www.cnblogs.com/longguoliang/p/9419322.html
Copyright © 2020-2023  润新知