《python源代码剖析》笔记 Python虚拟机框架
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie
1.
Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令。
并在当前的上下文环境中运行这条字节码指令。
Python虚拟机实际上是在模拟操作中运行文件的过程
PyCodeObject对象中包括了字节码指令以及程序的全部静态信息,但没有包括
程序执行时的动态信息——执行环境(PyFrameObject)
2.Python源代码中的PyFrameObject
- typedef struct _frame{
- PyObject_VAR_HEAD //"执行时栈"的大小是不确定的
- struct _frame *f_back; //执行环境链上的前一个frame。非常多个PyFrameObject连接起来形成执行环境链表
- PyCodeObject *f_code; //PyCodeObject 对象,这个frame就是这个PyCodeObject对象的上下文环境
- PyObject *f_builtins; //builtin名字空间
- PyObject *f_globals; //global名字空间
- PyObject *f_locals; //local名字空间
- PyObject **f_valuestack; //"执行时栈"的栈底位置
- PyObject **f_stacktop; //"执行时栈"的栈顶位置
- //...
- int f_lasti; //上一条字节码指令在f_code中的偏移位置
- int f_lineno; //当前字节码相应的源码行
- //...
- //动态内存,维护(局部变量+cell对象集合+free对象集合+执行时栈)所须要的空间
- PyObject *f_localsplus[1];
- } PyFrameObject;
名字空间实际上是维护着变量名和变量值之间关系的PyDictObject对象。
f_builtins, f_globals, f_locals名字空间分别维护了builtin, global, local的name与相应值
之间的映射关系。
每个 PyFrameObject对象都维护了一个 PyCodeObject对象。这表明每个 PyFrameObject中的动态内存空间
对象都和源码中的一段Code相相应。
Code Block。PyFrameObject,作用域,名字空间好像都与一段代码段一一相应?
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhlbmdzZW5saWU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
PyFrameObject中的动态内存空间
- PyFrameObject *PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyCodeObject *locals)
- {
- PyFrameObject *f;
- Py_ssize_t extras, ncells, nfrees, i;
- ncells = PyTuple_GET_SIZE(code->co_cellvars);
- nfrees = PyTuple_GET_SIZE(code->co_freevars);
- //这四部分构成了 PyFrameObject维护的动态内存区。其大小由extras确定
- extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
- f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
- //计算初始化时执行时栈的栈顶
- extras = code->co_nlocals + ncells + nfrees;
- //f_valuestack维护执行时栈的栈底,f_stacktop维护执行时栈的栈顶
- f->f_valuestack = f->f_localsplus + extras;
- f->f_stacktop = f->f_valuestack;
- return f;
- }
在创建 PyFrameObject对象时,额外申请的内存有一部分是给 PyCodeObject对象用的,
还有一部分才是给执行时栈用的。
在Python中訪问 PyFrameObject对象
8.1.3的caller.py我执行失败了。在f_globals.keys()处出错
看不懂frame_getter.py
3.名字、作用域和名字空间
对于python这类动态语言来说,名字的意义远比C这类的静态语言大。由于名字是python
在执行时可以找到其所相应的东西的唯一途径。
名字: 一个标识符就是一个名字,比方变量名、函数名、类名等
作用域:作用域是指约束起作用的程序正文区域。
Python是具有静态作用域(词法作用域)的。
在Python中,一个约束在程序正文的某个位置是否起作用。是由该约束在文本中的位置唯一决定的,
而不是执行时动态决定的。
名字空间:由一个 PyDictObject对象实现。每个名字空间都与一个作用域相应
module
1.将逻辑相关的代码放在一起,实现代码复用
2.为系统划分名字空间(一个module定义了一个独立的名字空间)
赋值语句会影响名字空间(“拥有赋值行为、拥有设置对象属性的行为”)
1.创建一个对象obj
2.将obj"赋给"一个名字name
- a = 1
- def f()
- class A(object)
- import abc
嵌套作用域最内嵌套作用域规则:1.闭包实现 2.编译器实现LEGB规则:名字引用动作沿着local作用域、嵌套作用域、global作用域、builtin作用域的顺序查找函数相应local作用域。module源文件相应global作用域,Python自身定义了最顶层的作用域——builtin作用域几个有助理解LEGB规则的代码段
- <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"></span><pre name="code" class="cpp">#----------------------
- a = 1 #[1]
- def f():
- a = 2 #[2]
- print a #[3] 輸出2
- print a #[4] 輸出1
- #----------------------
- #闭包。
- 在运行func = f()的时候。会运行函数f中的def g():语句,这时Python会将约束a = 2与函数g相应的函数对象捆绑在一起
- #将捆绑后的结果返回
- a = 1
- def f():
- a = 2
- def g():
- print a #[1]:输出结果为2
- return g
- func = f()
- func() #[2]
- #----------------------
- <pre name="code" class="cpp">#由一个赋值语句引进的名字在这个赋值语句所在的作用域是可见的。在 第9章 Python虚拟机中的一般表达式 这章有说明实现原理
- #[1]pint a语句处,a已经存在了这个Code Block相应的PyFrameObject相应的co_names符号表里了。但因为还没运行[2]的赋值语句,
- #在PyFrameObject的f_locals里并没有这个键值对,所以在[1]处会出现“local variable 'a' referenced before assignment”的错误
a
= 1def g():print adef f():print a #[1] error: local variable 'a' referenced before assignment a = 2 #[2]print ag()f()#----------------------a = 1def f():global aprint a #输出结果:1a = 2f()print a #输出结果:2#----------------------a = 1def f():a = 2def g():global aprint
a #输出结果:1a += 1return gg = f()g()print a #输出结果:2
4.Python虚拟机的执行框架——字节码执行引擎
PyEval_EvalFrameEx
虚拟机的详细实现-->switch/case结构
Python虚拟机运行字节码指令序列的过程就是从头到尾遍历整个co_code、依次运行字节码指令(字符数组)的过程。
- /* Interpreter main loop */
- PyObject* PyEval_EvalFrame(PyFrameObject *f)
- {
- //……
- why = WHY_NOT;//指示了在退出for循环时Python运行引擎的状态
- for (;;) {
- //……
- fast_next_opcode:
- f->f_lasti = INSTR_OFFSET();
- /* 获得字节码指令和參数*/
- opcode = NEXTOP();
- oparg = 0;
- /* 假设指令须要參数。获得指令參数*/
- if (HAS_ARG(opcode))
- oparg = NEXTARG();
- dispatch_opcode:
- switch (opcode) {
- case NOP:
- goto fast_next_opcode;
- case LOAD_FAST:
- //……
- }
- }
5.Python执行时环境初探
PyInterpreterState --> 进程的抽象
PyThreadState --> 线程状态信息的抽象
在Win32下,线程是还能独立存活的,它须要存活在进程的环境中。多个线程能够共享进程的一些资源
进程中会有线程集合,线程中会有函数调用堆栈。
当Python虚拟机開始运行时,会将当前线程状态对象中的frame设置为当前的运行环境(frame),而
在建立新的PyFrameObject对象时,则从当前线程的状态对象中取出旧的frame,建立PyFrameObject链表
线程之间的同步靠的是全局解释锁(GIL)
- //进程
- typedef struct _is{
- struct _is *next;
- struct _ts *tstate_head; //模拟进程环境中的线程集合
- PyObject *modules;
- PyObject *sysdict;
- PyObject *builtins;
- //...
- } PyInterpreterState
- //线程
- typedef struct _ts{
- struct _ts *next
- PyInterpreterState *interp;
- struct _frame *frame; //模拟线程中的函数调用堆栈
- int recursion_depth;
- //...
- PyObject *dict;
- //...
- long thread_id;
- } PyThreadState;
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhlbmdzZW5saWU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
《python源代码剖析》笔记 Python虚拟机框架的更多相关文章
- Python源代码剖析笔记3-Python运行原理初探
Python源代码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源代码剖析笔记,然而慢慢觉得没有从一个宏观 ...
- 《python源代码剖析》笔记 Python的编译结果
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.python的运行过程 1)对python源码进行编译.产生字节码 2)将编译结果交给p ...
- 《python源代码剖析》笔记 python虚拟机中的函数机制
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.Python虚拟机在运行函数调用时会动态地创建新的 PyFrameObject对象, 这 ...
- 《python源代码剖析》笔记 python环境初始化
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/zhsenl/article/details/33747209 本文为senlie原创.转载请保留此地 ...
- 《python源代码剖析》笔记 python中的List对象
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyListObject对象 --> 变长可变对象,可看作vector<Py ...
- 《python源代码剖析》笔记 python中的Dict对象
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyDictObject对象 --> C++ STL中的map是基于RB-tre ...
- python自动化测试学习笔记-9测试框架
学习了这么久的python,我们已经可以自己搭建一个简单的测试和框架了,先从简单的开始,有时我们编写接口的测试用例会用excel进行编写,以下面的接口测试用例模板为例,进行编写:
- Python知识点入门笔记——Python文件操作、异常处理及random模块使用
文件是存储在外部介质的数据集合,通常可以长久保存,前提是介质不易损坏 Python的绝对路径写法: E:\\编程学习资料\\爬取某社区高清无码大图.py E:/编程学习资料/爬取某社区高清无码大图.p ...
- Python知识点入门笔记——Python的基本数据类型
Python的数字分为4种类型:整数(int).浮点数(float).布尔值(bool).复数(complex). type()函数可以知道数据的类型,如type(233)是int型,type(233 ...
随机推荐
- ios数据的基本类型和流程控制
swift的声明变量方式和js是类似的.基本类型基本都和java的差不多,多了字符类型. let:用于声明常量: var:用于声明变量: 基本类型有:double,float,Int(数字类型):bo ...
- [ SCOI 2009 ] 最长距离
\(\\\) \(Description\) 一个\(N\times M\)的网格图中有一些坏点,图是四联通的. 你至多可以拿走\(K\)个坏点,求拿走后联通的点对中欧几里得距离最大是多少. \(N, ...
- CF126B Password
思路: kmp略作修改. 实现: #include <iostream> #include <cstdio> using namespace std; ; int neXt[M ...
- C#语言最基础的数组和集合
数组的书写格式:数据类型[]变量名=new 数据类型[长度]: 集合的书写格式:List<变量类型>变量名=new List<变量类型>(): 集合添加元素:变量名.Add(数 ...
- 巧用Eclipse Java编辑器调试
在使用Eclipse开发Java Web应用时,使用的编辑器不但能够为开发者提供代码编写.辅助提示和实时编译等常用功能,而且还能够对Java源代码进行快捷修改.重构和语法纠错等高级操作.通过Eclip ...
- Python3之切片的道理
list的切片有三个参数:起点,终点,步长 list[::-1] 相当于起点为最后的一个,终点为第一个,然后一次减少一个 更多的看下面的测试 >>> a = [0,1,2,3,4,5 ...
- Hash二次探测
Hash的二次探测,当hash的长度为n:插入val,当Hash[val]不为0时,选择新地址newval = val +(-) 1*1,val+(-)2*2,val+(-)(n-1)*(n-1); ...
- CNN结构:色彩空间建模-色彩空间分析
原文: 色彩空间基础 好一个NB的知乎专栏:色彩空间基础 第一章:色彩空间基础 关于色彩分析,引出了专门的数学基础.整个过程给出了完备的数学阐述,虽然没有试验数据,论述的相当精彩. 摘抄出一段: 上 ...
- (转)分布式文件存储FastDFS(四)配置fastdfs-apache-module
http://blog.csdn.net/xingjiarong/article/details/50560605 在前边我们已经配置好了FastDFS的环境,但是此时的FastDFS还不能通过htt ...
- codeforces_731D_(前缀和)(树状数组)
D. 80-th Level Archeology time limit per test 2 seconds memory limit per test 256 megabytes input st ...