11 - 函数的执行流程-函数递归-匿名函数-生成器

文章目录1 函数的执行流程1.1 字节码了解压栈过程1.2 嵌套函数的压栈2 递归2.1 递归函数2.2 递归的性能2.3 递归的优化2.4 间接递归2.5 递归总结3 匿名函数4 Python生成器4.1 基本结构4.2 使用场景4.3 协程coriutine4.4 yield from1 函数的执行流程函数的执行需要对函数进行压栈的,什么是压栈呢,简而言之就是在函数执行时在栈中创建栈帧存放...
摘要由CSDN通过智能技术生成

1 函数的执行流程

函数的执行需要对函数进行压栈的,什么是压栈呢,简而言之就是在函数执行时在栈中创建栈帧存放需要变量以及指针的意思。具体涉及的知识非常多,这里就已一个Python脚本简单进行分析。

def foo1(b, b1=3):
    print('call foo1', b, b1)

def foo2(c):
    foo3(c)
    print('call foo2', c)

def foo3(d):
    print('call foo3', d)

def main():
    print('call main')
    foo1(100, 101)
    foo2(20)
    print('main ending')

main()

当我们运行上面代码时,它的执行流程如下:

  1. 全局栈帧中生成foo1、foo2、foo3、main函数对象
  2. main函数调用
  3. main中查找内建函数print压栈,将常量字符串压栈,调用函数,弹出栈顶
  4. main中全局查找函数foo1压栈,将常量100、101压栈,调用函数foo1,创建栈帧。print函数压栈,字符串和变量b、b1压栈,调用函数,弹出栈顶,返回值。
  5. main中全局查找foo2函数压栈,将常量200压栈,调用foo2,创建栈帧。foo3函数压栈,变量c引用压栈,调用foo3,创建栈帧。foo3完成print函数调用返回。foo2恢复调用,执行print语句后,返回值。main中foo2调用结束后弹出栈顶,main继续执行print函数调用,弹出栈顶,main函数返回

1.1 字节码了解压栈过程

        Python 代码先被编译为字节码后,再由Python虚拟机来执行字节码, Python的字节码是一种类似汇编指令的中间语言, 一个Python语句会对应若干字节码指令,虚拟机一条一条执行字节码指令, 从而完成程序执行。Python dis 模块支持对Python代码进行反汇编, 生成字节码指令。下面针对上面的例子通过字节码理解函数调用时的过程。

import dis
print(dis.dis(main))

# ======> result
 53           0 LOAD_GLOBAL              0 (print)
              2 LOAD_CONST               1 ('call main')
              4 CALL_FUNCTION            1
              6 POP_TOP

 54           8 LOAD_GLOBAL              1 (foo1)
             10 LOAD_CONST               2 (100)
             12 LOAD_CONST               3 (101)
             14 CALL_FUNCTION            2
             16 POP_TOP

 55          18 LOAD_GLOBAL              2 (foo2)
             20 LOAD_CONST               4 (20)
             22 CALL_FUNCTION            1
             24 POP_TOP

 56          26 LOAD_GLOBAL              0 (print)
             28 LOAD_CONST               5 ('main ending')
             30 CALL_FUNCTION            1
             32 POP_TOP
             34 LOAD_CONST               0 (None)
             36 RETURN_VALUE

字节码含义:

  1. LOAD_GLOBAL:加载全局函数(print)
  2. LOAD_CONST: 加载常量
  3. CALL_FUNCTION: 函数调用
  4. POP_TOP:弹出栈顶
  5. RETURN_VALUE: 返回值

1.2 嵌套函数的压栈

def outer():
    c = 100
    def inner():
        nonlocal c
        c += 200
        return c
    return inner

a = outer()
a()
  1. 函数只有在执行的时候才会压栈,所以在outer执行时,会开辟栈空间压栈(c,inner)
  2. 执行完后,删除栈空间,但是由于outer返回了内部函数inner,但并没有执行,所以不会继续压栈,当执行a的时候,会重新压栈,而此时内部函数已经记住了外部自由变量
    c,并且每次调用outer都会重新生成一个inner。

注意:这种情况叫做闭包,自由变量c会被当成内部函数inner的一个属性,被调用。

PS:内存两大区域(栈,堆)。垃圾回收,清理的是堆中的空间。函数的调用就是压栈的过程,而变量的创建都是在堆中完成的。 栈中存储的都是堆中的内存地址的指向,栈清空,并不会使堆中的对象被清除,只是指向已经被删除。函数,变量都是在堆内创建的

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值