zoukankan      html  css  js  c++  java
  • 生成器原理分析

    生成器

      生成器是一种快速完成迭代器功能的工具,是一种特殊的迭代器。通过在函数中,设置关键字yield,即为生成器函数。

    def Student():
        yield 1
        yield 2
        yield 3
    

      为什么说生成器是一种特殊的迭代器,可以通过isinstance函数判断。

      可以看到,生成器是可迭代对象,也是迭代器对象。

      同样可以使用生成器来解决斐波那契数列的问题。

    def gen_fib(index):
        n,a,b = 0,0,1
        while n<index:
            yield b
            a,b = b, a+b
            n += 1
    
    for data in gen_fib(10):
        print (data)
    

    原理解释

      在讲生成器原理之前,我们需要先明白python函数的原理。我们的代码通过解释器Python.exe来完成,一般情况下,Python的底层由C语言编写,也就是我们常说的CPython。Python函数的执行需要借助由C语言编写好的PyEval_EvalFramEx函数,首先会创建好一个叫做stack frame的东西。stack frame(栈帧)也是一个对象,将代码会变成字节码对象。同时栈帧是放在堆内存上的。

      看到这各位看官可能会觉得有点抽象。下面以一个具体的例子来说明。栈帧我们可以使用inspect模块来研究。inspect可以从实时的Python对象中获得我们想要的信息。可以看到输出结果分别为levy()和student()函数的栈帧名。

    import inspect
    stack_frame = None #栈帧变量
    def student():
        levy()
        return "i am a student"
    
    def levy():
        global stack_frame #定义一个全局变量
        stack_frame = inspect.currentframe() #获得当前的栈帧
    
    student() #调用student()函数后生成栈帧
    print(stack_frame.f_code.co_name) #栈帧的名字
    last_stack_frame = stack_frame.f_back #上一个栈帧
    print(last_stack_frame.f_code.co_name) #输出上一个栈帧的名字
    


      通过一张图我们很容易理清其中的关系,上面为student()函数,下面为levy()函数。

      借助dis模块我们可以将Python字节码反汇编成助记符,用于分析案例的原理。参考下图,LOAD_GLOBAL加载函数levy(), LOAD_CONST加载return的返回值"i am a student'。

    import inspect,dis
    stack_frame = None #栈帧变量
    def student():
        levy()
        return "i am a student"
    
    def levy():
        global stack_frame #定义一个全局变量
        stack_frame = inspect.currentframe() #获得当前的栈帧
    dis.dis(student) #dis模块的使用
    


      如果理解了上面的原理的话,我们下面再来看生成器的原理分析。生成器中的堆内存存储了GenObject指向了帧对象和字节码对象。

      生成器原理的示例如下,我们输出反编译后的助记符行号以及本地存储变量(字典形式),通过不断的调用next()函数,来顺序访问生成器函数中的对象。

    def stu():
        yield "hello"
        student = 'levy'
        yield "world"
        return "i am a student"
    import dis
    xiaoming  = stu()
    dis.dis(xiaoming)
    
    print(xiaoming.gi_frame.f_lasti)#字节码对应行数
    print(xiaoming.gi_frame.f_locals)#本地变量字典形式
    next(xiaoming)#执行下一个
    print(xiaoming.gi_frame.f_lasti)
    print(xiaoming.gi_frame.f_locals)
    next(xiaoming)
    print(xiaoming.gi_frame.f_lasti)
    print(xiaoming.gi_frame.f_locals)
    

      -1表示生成器函数还未开始执行,存储的变量为空{},2表示执行到第二行YIELD_VALUE返回了一个"hello"字符串,同理12表示第十二行YIELD_VALUE返回了一个"world"字符串。生成器的运行大致如我所说,通过惰性计算,每次返回一个所需的值,并且能够记住代码的执行位置,可以对整个生成器的暂停和前进进行控制。

  • 相关阅读:
    【小记事】如何设置vscode代码格式化时不要自动换行
    ES6的Array.from()和Array.fill()方法
    Android Studio Run/Debug configuration error: Module not specified
    合并PPT_Powerpint文件_保持主题颜色/Merge PowerPoint Keep Source Format
    Matlab 文件格式化/Matlab Source File Formattor
    Look into Bitmap images
    pdf2eps implement
    LyX Error convert to loadable format
    Fuzzy模糊推导(Matlab实现)
    Batch批处理获取当前时间
  • 原文地址:https://www.cnblogs.com/7levy/p/11420675.html
Copyright © 2011-2022 走看看