• 08-迭代器和生成器


    一、迭代器和生成器

      迭代是Python最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。迭代器有两个基本的方法:iter() 和 next()。

      在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。

    1.1、python中的迭代协议

      什么是迭代协议?  迭代协议:一个是 __iter__(也就是可迭代类型),另一个是Iterator(迭代器),迭代器是什么? 迭代器是访问集合内元素的一种方式, 一般用来遍历数据。迭代器和以下标的访问(list[0])方式不一样, 迭代器是不能返回的, 迭代器提供了一种惰性数据的方式。

    #list[item]访问的原理是__getitem__
    #[] list , __iter__
    #Iterable继承的是抽象基类(metaclass=ABCMeta)(__iter__)
    #Iterator(迭代器)这个是继承了Iterable,实现的魔法函数有__iter__,__next__
    from collections.abc import Iterable, Iterator
    a = [1,2]  #list是实现__iter__是可迭代类型,可迭代类型与迭代器(__iter__,__next__)是不一样的
    print(isinstance(a, Iterable))#True
    print(isinstance(a, Iterator))#False

    1.2、什么是迭代器和可迭代对象

    from collections.abc import Iterator
    
    class Company(object):
        def __init__(self, employee_list):
            self.employee = employee_list
    
        def __iter__(self):
            return MyIterator(self.employee) #返回的是迭代器,这样自定义迭代器复用性更强
    
        # def __getitem__(self, item): #能切片,迭代器是不能切片的
        #     return self.employee[item]
    
    
    class MyIterator(Iterator):
        def __init__(self, employee_list):
            self.iter_list = employee_list
            self.index = 0 #用迭代器维护这个变量
    
        def __next__(self):
            #真正返回迭代值的逻辑
            try:
                res= self.iter_list[self.index]
            except IndexError:
                raise StopIteration
            self.index += 1
            return res
    
    if __name__ == "__main__":
        company = Company(["li", "lily", "john"])
        my_itor = iter(company)
        # while True:           #for循环的实现原理
        #     try:
        #         print (next(my_itor))
        #     except StopIteration:
        #         pass
    
        # 当调用迭代器的时候会这样调用next(my_itor)
        for item in company: #当调用for循环的时候,它会尝试去调用iter(company),首先找的是__iter__,
            print(item)       # 如果没有的话,它就会找__getitem__,

    1.3、生成器函数的使用

      生成器函数,函数里只要有yield关键字。

    def gen_func():#提交的值都能for循环打印出来
        yield 1
        yield 2
        yield 3
    
    def fib(index):  #1求斐波拉契只能看见最终结果,看不到过程
        if index <= 2:
            return 1
        else:
            return fib(index-1) + fib(index-2)
    
    def fib2(index): #2改动之后,将每次产生的值添加到列表中,就可以看到过程中产生的值
        re_list = []
        n,a,b = 0,0,1
        while n<index:
            re_list.append(b)
            a,b = b, a+b
            n += 1
        return re_list
    
    def gen_fib(index):#3用生成器改动,将每次结果yield出来,
        n,a,b = 0,0,1
        while n<index:
            yield b
            a,b = b, a+b
            n += 1
    
    # 斐波拉契 0 1 1 2 3 5 8
    #惰性求值, 延迟求值提供了可能
    
    def func():
        return 1
    
    if __name__ == "__main__":
        #生成器对象, python编译字节码的时候就产生了,
        gen = gen_func()
        for value in gen:
            print (value,end=' ') #1 2 3
        # re = func()
        #将yield提交的值遍历出来
        for data in gen_fib(10):
            print(data,end=' ') #1 1 2 3 5 8 13 21 34 55 

    1.4、python是如何实现生成器的

    #1.python中函数的工作原理
    
    import inspect
    frame = None
    def foo():
        bar()
    def bar():
        global frame
        frame = inspect.currentframe()
    
    #python.exe会用一个叫做 PyEval_EvalFramEx(c函数)去执行foo函数, 首先会创建一个栈帧(stack frame)
    """
    python一切皆对象,栈帧对象,编译成字节码对象
    当foo调用子函数 bar, 又会创建一个栈帧
    所有的栈帧都是分配在堆内存(栈帧一直在堆内存之中)上,这就决定了栈帧可以独立于调用者存在
    """
    # import dis
    # print(dis.dis(foo)) #查看运行的字节码过程
    
    foo()
    print(frame.f_code.co_name) #bar
    caller_frame = frame.f_back
    print(caller_frame.f_code.co_name) #foo  #虽然这个函数已经运行完成,我们照样可以去堆内存中找到它的栈帧
    
    
    def gen_func():
        yield 1
        name = "lishuntao"
        yield 2
        age = 18
        return "HUT"
    
    import dis
    gen = gen_func() #返回生成器对象
    print (dis.dis(gen))
    #人为控制它的执行流程
    print(gen.gi_frame.f_lasti) #-1      f_lasti:可以知道进行到哪一个步骤了
    print(gen.gi_frame.f_locals) #{}
    next(gen)  #进行下一步流程
    print(gen.gi_frame.f_lasti)#2
    print(gen.gi_frame.f_locals)#{}
    next(gen)
    print(gen.gi_frame.f_lasti)#12
    print(gen.gi_frame.f_locals)#{"name":"lishuntao"}

     

    1.5、生成器在UserList中的应用

    class Company:
        def __getitem__(self, item):
            pass
        def __iter__(self):
            pass
    company = Company()
    iter(company)  #调用iter的时候,会去对象中寻找__iter__,如果没有的话就去寻找__getitem__
    
    #生成器在list中的应用,如果我们要继承list的话,就继承UserList,因为list是C语言写出来的,UserList是利用python写出来的。
    from collections import UserList #UserList中继承的序列Sequence中的__iter__里面就运用生成器实现的数据迭代

    1.6、生成器如何读取大文件

    当在工作中,应用生成器提取大文件(一行大文件)。

    #大文件, 特殊的一行
    def myreadlines(f, newline):
      buf = ""     #设置空的缓存区
      while True:
        #第一次进入buf为空,直接跳到下面读取文件
        while newline in buf:
          pos = buf.index(newline)
          yield buf[:pos]  #提交第一行
          buf = buf[pos + len(newline):] #将提交出去的舍弃掉
        chunk = f.read(4096)
    
        if not chunk:
          #说明已经读到了文件结尾,然后将buf提交出去
          yield buf
          break
        buf += chunk
    
    with open("input.txt") as f:
        for line in myreadlines(f, "{|}"):
            print (line)
    """
    Prior to beginning tutoring sessions
    , I ask new students to fill
     out a brief self-assessment
     where they rate their
     understanding of various Python concepts. Some topics ("control flow with if/else" or "defining and using functions") are understood by a majority of students before ever beginning tutoring. There are a handful of topics, however, that almost all
     students report having no knowledge or very limited understanding of. Of these
    """
  • 相关阅读:
    IOS开发---菜鸟学习之路--(二十四)-iOS7View被导航栏遮挡问题的解决
    IOS开发---菜鸟学习之路--(二十三)-直接利用键值对的方式来处理数据的感想
    IOS开发---菜鸟学习之路--(二十二)-近期感想以及我的IOS学习之路
    一口一口吃掉Hexo(六)
    一口一口吃掉Hexo(二)
    setSupportActionBar(toolbar)导致程序崩溃闪退
    【原创+译文】官方文档中声明的如何创建抽屉导航栏(Navigation Drawer)
    【文章内容来自《Android 应用程序开发权威指南》(第四版)】如何设计兼容的用户界面的一些建议(有删改)
    如何为按钮设置一组不同状态的颜色
    如何避免Activity 被杀死
  • 原文地址:https://www.cnblogs.com/lishuntao/p/12018887.html
Copyright © 2020-2023  润新知