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)
  • 相关阅读:
    C#利用反射动态调用类及方法
    系统程序监控软件
    SQL server 2008 安装和远程访问的问题
    sql server 创建临时表
    IIS 时间问题
    windows 2008 安装 sql server 2008
    sql server xml nodes 的使用
    Window 7sp1 安装vs2010 sp1 打开xaml文件崩溃
    CSS资源网址
    Could not load type 'System.ServiceModel.Activation.HttpModule' from assembly 'System.ServiceModel, Version=3.0.0.0
  • 原文地址:https://www.cnblogs.com/yc3110/p/10458663.html
Copyright © 2011-2022 走看看