1.python中函数的工作原理

def foo():bar()def bar():pass

python的解释器,也就是python.exe(c编写)会用PyEval_EvalFramEx(c函数)运行foo()函数
首先会创建一个栈帧(stack Frame),在栈帧对象的上下文里面去运行这个字节码。

import dis
print(dis.dis(foo))  #打印字节码

可以尝试着去打印foo的字节码:

关于字节码的解释:

  • LOAD_GLOBAL:首先导入bar这个函数
  • CALL_FUNCTION:执行bar函数
  • POP_TOP:从栈的顶端去把元素打印出来
  • LOAD_CONST:返回结果,这里没有return,就是None
  • RETURN_VALUE:返回结果

打印bar的字节码:

print(dis.dis(bar))


这个字节码全局是唯一的,函数是全局唯一的,然后在函数里面会调用另外一个函数。

当foo调用函数bar,又会创建一个栈帧,然后将这个函数的控制权交给这个栈帧。

所有的栈帧都分配在内存中,它不是放在栈的内存上,而是放在堆的内存上,你不去释放它就会一直存在我们的内存当中。

这就决定了栈帧可以独立于调用者存在,比如就算函数不存在了,只要有指针指向bar这个栈帧,就可以对其进行控制。

(python中一切皆对象,栈帧也是对象,是一个字节码对象)

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import inspect
frame = None  #保存framedef foo():bar()def bar():global frame  #引入全局变量frame = inspect.currentframe()  #将bar的frame赋给全局变量foo()
print(frame.f_code.co_name)  #bar  函数退出之后,依然可以拿到bar函数的栈帧
caller_frame = frame.f_back
print(caller_frame.f_code.co_name)  #foo  也可以拿到foo函数的栈帧

2.生成器的实现原理

在静态语言中,函数调用的时候是一个栈的形式,函数调用完成之后栈就会被销毁。
下面是函数的调用过程:

PyEval_evalFrameEx会创建一个foo的栈帧对象,这个对象里面有两个属性。f_back为None,因为没有上层函数,f_code指向foo的字节码
同时PyEval_evalFrameEx也会创建一个bar的栈帧对象,f_back指向foof_code指向bar的字节码。
最大的特点就是栈帧对象存在于堆内存中,这样生成器才有实现的可能。

def gen_func():yield 1name = "ming"yield 2age = 28return "kebi"  #在早期的生成器版本中不能使用return

当python解释器在读取gen_fun()这个函数的时候,发现yield关键字就会将其标记为生成器函数。

gen_func()

当我们来调用这个函数的时候,就会返回一个生成器对象。

这个生成器对象是将PyFrame做了一层封装。


在PyFrameObject和PyCodeObject上面又封装了一层PyGenObject,就是python的生成器对象。

PyGenObject中gi_frame属性指向PyGrameObject,gi_code属性指向PyCodeObject。

PyFrameObject又有f_lastif_locals属性。

f_lasti会指向最近执行的这个代码。

可以尝试打印字节码:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def gen_func():yield 1name = "ming"yield 2age = 28return "kebi"  #在早期的生成器版本中不能使用returnimport dis
gen = gen_func()
print(dis.dis(gen))

查看结果:

这里面可以看到有两次yield。当我们每一次对生成器做一次调用的时候,它遇到yield就会停止。
停止了之后,就会记录f_lasti(位置)和f_locals(变量)这两个值。

可以尝试着调用打印取每一个值,f_lastif_locals的变化

print(gen.gi_frame.f_lasti)  #-1
print(gen.gi_frame.f_locals) #{}
next(gen)
print(gen.gi_frame.f_lasti)  #2
print(gen.gi_frame.f_locals) #{}
next(gen)
print(gen.gi_frame.f_lasti)  #12
print(gen.gi_frame.f_locals) #{'name':'ming'}

与上方字节码是一样的。

这样整个生成器对象就存在与堆内存中,可以独立存在,每次执行一次函数,就会生成一个栈帧对象。
我们可以在任何地方,只要能拿到这个栈帧对象就能够往前走。这也是python中协程的一个理论基础。

此时我们可以知道为什么生成器是一个一个返回。

3.pyc文件

当你在执行python代码的时候,会发现执行目录下面会出现.pyc文件。

[root@tuoguan resources]# ls
r1.py  r1.pyc  r2.py  r3.py
[root@tuoguan resources]# cat r1.pyc¶:]c@s
dZdS(tname_r1N(R(((s/tmp/demo/resources/r1.py<module>s

r1.pyc是一个二进制文件,当执行的文件中存在包的引入就会编译生成二进制文件。

当python程序运行时,编译的结果则是保存在位于内存中的PyCodeObject中,当Python程序运行结束时,Python解释器则将PyCodeObject写回到pyc文件中。

当python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复上面的过程。

所以我们应该这样来定位PyCodeObject和pyc文件,我们说pyc文件其实是PyCodeObject的一种持久化保存方式。

python中的函数、生成器的工作原理相关推荐

  1. python有关迭代器和生成器的面试题_【面试题 | Python中迭代器和生成器的区别?】- 环球网校...

    [摘要]今天给大家解答一道Python常见的面试题,希望这个面试栏目,给那些准备面试的同学,提供一点点帮助!小编会从最基础的面试题开始,每天一题.如果参考答案不够好,或者有错误的话,麻烦大家可以在留言 ...

  2. SICP2——Python中使用函数构建对象

    一.使用对象构建抽象 1.1 数据抽象 现在到了数学抽象中最关键的一步:让我们忘记这些符号所表示对象.-根本不必考虑它们到底代表着什么东西. 上一篇文章主要强调的是对数据的操作以及这些操作之间的组合与 ...

  3. python中定义函数常用关键字_Python 中定义函数的关键字是 _________________ 。_学小易找答案...

    [其它]实验4-串和数组-实验任务书.docx [填空题]表达式 'abc' in ['abcdefg'] 的值为______________. [填空题]已知 x = range(1,4) 和 y ...

  4. python中max函数用法_Python中max函数用法实例分析

    Python中max函数用法实例分析 更新时间:2015年07月17日 15:45:09 作者:优雅先生 这篇文章主要介绍了Python中max函数用法,实例分析了Python中max函数的功能与使用 ...

  5. python中format函数用法简书_从Python安装到语法基础,这才是初学者都能懂的爬虫教程...

    Python和PyCharm的安装:学会Python和PyCharm的安装方法 变量和字符串:学会使用变量和字符串的基本用法 函数与控制语句:学会Python循环.判断语句.循环语句和函数的使用 Py ...

  6. python中bin函数如何使用?

    二进制对于大家再熟悉不过了,它是我们计算机编程中必要的语言,只有将其他进制转换为二进制,计算机才得以工作.本文主要介绍能够返回二进制表示的bin()函数,bin()函数用于获取数字的二进制值,接受数字 ...

  7. python中reduce函数的运用_python 中 reduce 函数的使用

    reduce()函数也是Python内置的一个高阶函数. reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接 ...

  8. python中set()函数的用法,python中set()函数简介及实例解析

    python中set()函数简介及实例解析 set函数也是python内置函数的其中一个,属于比较基础的函数.其具体介绍和使用方法,下面进行介绍. set() 函数创建一个无序不重复元素集,可进行关系 ...

  9. python中eval函数和int函数功能一样_python中eval与int的区别浅析

    python中eval和int的区别是什么?下面给大家介绍一下: 1.eval()函数 eval()能够以Python表达式的方式解析并执行字符串,并将返回结果输出.eval()函数将去掉字符串的两个 ...

  10. Python中Print()函数的用法___实例详解(二)(全,例多)

    Python中Print()函数的用法___实例详解(二)(全,例多) 目录 十一.Print()小例子 十二.Print()中文输入显示乱码问题 十三.Print()写入文件 十四.print()在 ...

最新文章

  1. xcode新版本single view_动态数组函数系列1|概况-跟以往Excel版本完全不一样玩法的函数...
  2. php双分支语句【三个数排序】
  3. JSON学习资料整理
  4. flexbox布局_Flexbox vs Grid-如何构建最常见HTML布局
  5. 【阿里云 MVP 月度分享】宋亚奇——应用MaxCompute实现电力设备监测数据的批量特征分析...
  6. geteditor p 取消自动_自动挡汽车最热问题,N档到底是干什么用的?
  7. 前端 如何检测到当前的网页已经退出_javascript在当前窗口关闭前检测窗口是否关闭...
  8. 梭子鱼下一代防火墙在对比研究中名列榜首
  9. springboot easyexcel 导出excel案例及文件无法打开
  10. Js中字符串转Json与Json对象转字符串
  11. coding部署博客 + 腾讯云 cdn 踩坑日记
  12. 垃圾分类图片数据集分享-约10w张数据集
  13. ACL2021_Enhancing Entity Boundary Detection for Better Chinese Named Entity Recognition
  14. resulful规范_ResultFul API
  15. 分享几个在线生成头像的网站
  16. silk 编解码_SILK编码语音转WAV格式
  17. 专为医疗领域打造!飞凌嵌入式新一代FDU显控一体机发布
  18. 电子邮件协议详解(SMTP、POP3、IMAP4)
  19. 郑大计算机研究生学硕好还是专硕好,2021郑州大学考研:学硕专硕的区别
  20. python switch to frame_Switch to Frame

热门文章

  1. MyEclipse将Java项目打包成jar文件的三种方法
  2. ASP数据库插马小议
  3. 【Vegas原创】获取远程数据库到本地
  4. 【整理】SAP系统内核和ABAP版本
  5. 自定义维护视图变量(Maintenance view variant)
  6. 把内表 itab1 的 n1 到 n2 行内容附加到 itab2 内表中去.
  7. 事务RFC(TRFC)原理和实战解析
  8. 致远表单代办状态删除
  9. SAP中委外采购订单发料
  10. SAP中smartforms参数