zoukankan      html  css  js  c++  java
  • Python是如何实现生成器的原理

    python中函数调用的实质原理:

      python解释器(即python.exe)其实是用C语言编写的, 在执行python代码时,实际上是在用一个叫做Pyeval_EvalFramEx(C语言的函数)去执行代码中的函数,(实际上python中的程序实际上是运行在C语言之上的),运行此函数的时候,首先会在内存的堆区创建一个栈帧(stack frame),python中一切皆对象,在栈帧中间将要执行的代码编译成为字节码对象。 然后在栈帧的上下文中去运行字节码,可以用dis.dis()函数查看函数的字节码。

      

    import dis
    def foo():
        bar()
    def bar():
        pass
    print(dis.dis(foo))
    
    结果:
     20           0 LOAD_GLOBAL              0 (bar)
                  2 CALL_FUNCTION            0
                  4 POP_TOP
                  6 LOAD_CONST               0 (None)
                  8 RETURN_VALUE
    None

      

      字节码解释:当foo调用子函数bar时候,又会创建一个栈帧,然后将函数的控制权交给新的栈帧,然后去运行bar的字节码,然后就有了两个栈帧了。因为所有的栈帧都是分配在堆的内存上,(函数调用完毕不会被立即回收)这就决定了栈帧可以独立于调用者存在,(即foo即使不存在了退出了也没有关系,只要有指针指向bar的栈帧,就可以对其进行控制)具体看一下代码:foo()调用完毕了之后,由于全局变量指向了bar中的栈帧对象,所以print(frame.f_code.co_name)语句输出产生当前栈帧对象的对象名,即bar,然后caller_frame = frame.f_back语句将调用者(foo)的栈帧对象获取到,然后打印出来,即foo。

    import inspect
    import dis
    
    frame = None
    def foo():
        bar()
    def bar():
        global frame
        frame = inspect.currentframe()
    foo()
    print(frame.f_code.co_name)
    caller_frame = frame.f_back
    print(caller_frame)
    
    输出结果:
    bar
    foo

     图解如下:heap(堆区), recurse(递归,图中意思即foo递归调用了bar)

    堆区的PyFrameObject表示生成的栈帧对象,f_code表示执行函数的字节码,f_back表示调用者函数的字节码。

    生成器的实现原理:


    
    
    def gen_fun():
        yield 'a'
        name = 'bobby1'
        yield 'b'
        age = 30
        return 'frank'
    import dis
    gen = gen_fun()
    print(dis.dis(gen)) #打印gen函数的字节码
    print(gen.gi_frame.f_lasti)
    print(gen.gi_frame.f_locals)
    next(gen)
    print(gen.gi_frame.f_lasti)
    print(gen.gi_frame.f_locals)
    next(gen)
    print(gen.gi_frame.f_lasti)
    print(gen.gi_frame.f_locals)
    
    输出结果:
    20           0 LOAD_CONST               1 ('a')
                  2 YIELD_VALUE
                  4 POP_TOP
    
     21           6 LOAD_CONST               2 ('bobby1')
                  8 STORE_FAST               0 (name)
    
     22          10 LOAD_CONST               3 ('b')
                 12 YIELD_VALUE
                 14 POP_TOP
    
     23          16 LOAD_CONST               4 (30)
                 18 STORE_FAST               1 (age)
    
     24          20 LOAD_CONST               5 ('frank')
                 22 RETURN_VALUE
    None
    -1
    {}
    2
    {}
    12
    {'name': 'bobby1'}

     真是因为有yield实现生成器函数,使得我们可以自由控制函数的运行于暂停,这个是协程实现的基础。

    生成器的应用实例:用生成器函数读取一行的超大文件。

    def yield_str(file, spilt):
        buf = ''
        while True:
    
            while spilt in buf:
                pos = buf.index(spilt)
                yield buf[:pos]
                buf = buf[pos + len(spilt):]
            chunk = file.read(100)
            if not chunk:
                yield buf
                break
            buf += chunk
    
    with open('txt.txt', encoding='utf-8') as file:
        for i in yield_str(file, ','):
            print(i)
  • 相关阅读:
    微信小程序中 数据缓存
    javascript 数组去重方法
    常用网站 ---总结
    vue面试题
    vue创建项目(命令方式)
    shutil 模块:文件复制.移动.压缩.解压.递归删除文件夹
    面向对象一些零散的知识点
    面向对象之多态
    面向对象与继承
    序列化与反序列化的三个模块:json pickle shelve
  • 原文地址:https://www.cnblogs.com/yc3110/p/10458663.html
Copyright © 2011-2022 走看看