python函数调用过程_函数的调用过程

函数的调用过程

1. 函数调用

函数的执行过程从表面上看只是被调用的过程,但它的背后却是用栈这种数据结构实现的。理解了函数的调用过程,递归也就变得容易理解。

下面用一段代码来展示函数的调用

def a():

print("进入函数a")

c()

print("函数a执行结束")

def b():

print("进入函数b")

c()

print("函数b执行结束")

def c():

print("进入函数c")

a()

执行代码,输出结果

进入函数a

进入函数c

函数a执行结束

函数a的执行过程分为3个步骤:

print("进入函数a")

c() 执行函数c

print("函数a执行结束")

这段代码很简单,但却隐藏着初学者所不知道的秘密,这也是本文所要讲解的重点,首先请你思考一个看起来有点无聊的问题,为什么在执行完第2步以后,会执行第3步,输出"函数a执行结束",而不是在执行完函数c以后,来到函数b中执行print("函数b执行结束")?

你当然可以回答,这是因为代码调用了函数a,所以函数c执行后要返回到a中而不是返回到函数b中。这个解释很合理,但是并没有说明根本原因,为什么函数c执行结束后要回到函数中,而不是函数b中?换一种问法,如果单独执行函数c,函数执行结束后既不会回到函数a,也不会回到函数b,为什么在函数a中执行函数c,函数c直接结束后就要回到函数a呢?

2. 函数调用与栈

在函数a中调用函数c,函数c执行结束后程序要回到函数a中继续执行,我们可以大胆猜测,一定是在某个地方记录了是在函数a中调用执行了函数c,所以才能准确的找到回去的路线,这个记录函数调用信息的地方就是栈。

栈是一种先进后出的数据结构,本文需要你对栈这种数据结构有一定的了解,否则,接下来要讲述的内容你很难理解。

在调用函数时,解释器会将函数的调用信息保存到栈中,这些保存的信息包括:

函数在第几行代码被执行

函数所在脚本

函数里的局部变量

在python中,可以通过sys._getframe()来查看这些信息

import sys

def a(count):

b(count - 1)

print(count)

def b(count):

c(count - 1)

print(count)

def c(count):

c_frame = sys._getframe() # 函数c的frame

print(c_frame.f_code, c_frame.f_lineno, c_frame.f_locals)

b_frame = c_frame.f_back # 函数b的frame

print(b_frame.f_code, b_frame.f_lineno, b_frame.f_locals)

a_frame = b_frame.f_back # 函数a的frame

print(a_frame.f_code, a_frame.f_lineno, a_frame.f_locals)

print(count)

a(3)

我在函数c的第2行代码设置断点并进行调试,通过观察变量frame可以获得函数的调用信息,代码输出结果为

13 {'c_frame': , 'count': 1}

8 {'count': 2}

4 {'count': 3}

1

2

3

f_code 保存代码信息

f_lineno 保存当前函数执行到第几行,也叫函数返回地址

f_locals 保存当前函数局部变量

函数的调用信息保存在frame结构中,通过f_back可以获得上一层函数的调用信息。

3. 函数调用过程

在调用函数时,会将当前信息保存到栈中,这其中就包括当前执行到第几行(f_lineno),当前的上下文环境(f_locals),当函数执行结束后,解释器则根据这些信息进行调度,它要根据f_lineno找到下一行要执行的代码,同时根据f_locals来还原现场。仍然以第2小节中的内容来透视函数的调用过程

function_frame-1583239758-0.jpg

函数逐个调用的过程中,每一次调用都会向栈里压如一次有关函数调用的信息,这其中最重要的就是f_lineno 和 f_locals,一个记录当前函数调用发生在第几行,一个记录当前环境下的变量信息,前者是为了在函数执行结束后找到回来的位置,后者是为了回来以后还原上下文环境。

当c函数执行结束后,栈顶的c_frame被移除,此时栈顶保存的信息是b_frame,b_frame保存的信息包括

8 {'count': 2}

通过frame里信息可以得知,上一次函数调用发生在demo2.py的第8行,因此下一行代码应该执行第9行代码,通过f_locals得知,count的值应该是2,利用f_locals里的信息,还原了现场环境,最终print(count)的输出结果是2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值