python并不将py文件编译为机器码来运行,而是由python虚拟机一条条地将py語句解释运行,这也是为什么被称为解释语言的原因之一。但python虚拟机并不直接执行py語句,它执行编译py語句后生成的字节码。本篇简单地讲下编译、运行的过程,涉及到的内容有如何编译、控制流、函数及类的实现等。

0. python的编译

python将py文件编译成为PyCodeObject,再将这个对象写入某文件就成为了pyc文件,文件中包含python的magic number(来说明编译时使用的python版本号)、源文件的mtime(使pyc和py文件保持同步)、编译出的code对象。将对象写入到一个文件似乎听起来不太可能,不过其实很简单,python只写入特定类型的对象,比如要写入一个code对象,python会按一定的顺序将这个对象中的属性一一写入,由于对象是固定的,因而只要记下写入时的顺序就可以从文件中恢复出对象。注意我们这里谈论的并不是python中的序列化。python在写入实际的对象时会写入标识符,即可以标明对象的边界,又可以保持内容信息以在内存中恢复出对象。

python将对象写入文件最后会将调用到两个函数,一个用来写个int,一个用来写入string。对于string来说,为了达到和intern机制一样的目的不重复写string,在写入的时候python还会维持一个dict来保存已经写入的被intern处理后的string,因此当一个string被intern处理过并且出现在之前所说的dict中时,python仅仅会写入该string的索引值,即它是第几个string。当读取文件时,python则会根据每个string的索引值重建一个list,碰到重复的intern的string时就可以根据索引值读出该string的值了。

1. python虚拟机基础

python虚拟机的执行方式就是模仿普通x86可执行文件运行方式,也有栈、帧等概念。除止之外,python还有一个执行环境的问题,考虑print a句话,a肯定指向了一个对象,但是是什么对象呢,这个就由执行环境来确定了,語句可以通过执行环境读写变量的值,在源代码中对这个执行环境的模拟是对它PyFrameObject来完成的,每一个code block都对应一个执行环境,就是一个PyFrameObject,可以认为是一帧。这个struct比较复杂,看下代码:

typedef struct _frame {

PyObject_VAR_HEAD

struct _frame

*f_back;  /* previous frame, or NULL */

PyCodeObject *f_code;   /* code

segment */

PyObject *f_builtins;   /* builtin symbol table (PyDictObject)

*/

PyObject *f_globals;    /* global symbol table (PyDictObject)

*/

PyObject *f_locals;     /* local symbol table (any mapping) */

PyObject **f_valuestack;    /* points after the last local */

/* Next

free slot in f_valuestack.  Frame creation sets to f_valuestack.

Frame

evaluation usually NULLs it, but a frame that yields sets it

to the

current stack top. */

PyObject **f_stacktop;

PyObject

*f_trace;      /* Trace function */

/* If an exception is raised in

this frame, the next three are used to

* record the exception info (if

any) originally in the thread state.  See

* comments before

set_exc_info() -- it's not obvious.

* Invariant:  if _type is NULL, then

so are _value and _traceback.

* Desired invariant:  all three are NULL,

or all three are non-NULL.  That

* one isn't currently true, but "should

be".

*/

PyObject *f_exc_type, *f_exc_value,

*f_exc_traceback;

PyThreadState *f_tstate;

PyTryBlock

f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */

PyObject

*f_localsplus[1];  /* locals+stack, dynamically sized */

}

PyFrameObject;

许多个PyFrameObject通过f_back连成一串链表,表示了帧与帧之间的先后、调用顺序。python中帧在运行时需要额外的内存,比如a = b +

c这段代码,那么需要先请读入b、c,再算a,除此之外还有局部变量等需要保存在栈中,因此最后有一个f_localsplus指向这块多出来的内存,大小则在编译时计算出来保存在f_stacksize中,这块内存具体用来依次保存locals(局部变量)、cellvars、freevars(后两个和闭包的实现有关)、动态栈(f_valuestack指向栈底、起始位置,f_stacktop则维持栈顶)。

当Python虚拟机开始执行时,它会先进行一些初始化操作,最后进入PyEval_EvalFramEx函数,它的作用是不断读取编译好的字节码,并一条一条执行,类似CPU执行指令的过程。函数内部主要是一个switch结构,根据字节码的不同执行不同的代码。

2. Python运行环境及执行过程

先从整体上看看Python的运行环境。我们知道在操作系统中执行程序离不开两个概念:进程和线程,在Python中也是这样,Python模拟了这两个概念。模拟进程或线程的分别是PyInterpreterState和PyThreadState。可以想象,每个PyThredState都对应着一个帧栈,Python虚拟机在多个线程上切换。

当Python虚拟机开始执行时,它会先进行一些初始化操作,最后进入PyEval_EvalFramEx函数,它的作用是不断读取编译好的字节码,并一条一条执行,类似CPU执行指令的过程。函数内部主要是一个switch结构,根据字节码的不同执行不同的代码。用一张图表示:

3. Python的名字空间

名字空间在Python中是一个非常重要的概念,读写变量值其实就是到某个名字空间中读写与该变量名相对应的对象。如果我们把某个符号和与之关联的对象之间的关系(就是(name,

value)这样的关联关系)称为約束的话,那可以认为名字空间的内容就是由一组组約束构成,而增加約束的語句可以被称为赋值語句,函数定义、类定义等都可以被称为是赋值語句。当一个module被加载后,Python会执行相应的代码在这个module的名字空间中建议相应的約束,然后就可以用module.attr这样的語句来访问module的属性。

对于每个module来说,它都有一个顶层的名字空间,可以认为是global名字空间,同时也没有哪个名字空间能够跨module存在。具体到module的某行代码,对它来说还存在local名字空间(比较在函数体或类定义中)、enclosing名字空间(用来实现闭包,其实它不真的存在,但假定有这样一个更好理解)。而Python内置了builtin名字空间。如果要查找某个符号的话,Python会依次查找LEGB。要注意的是Python具有静态作用域,它的意思就是说名字空间这个东西是在你写好py代码后就确定的,而不是由执行的时候确定的。这一部分对于理解Python非常重要,由于篇幅所限之前在是不能详述,建议查看《Python源码剖析》第8.2节。

python虚拟机详解_Python 虚拟机实现(一)相关推荐

  1. python录音详解_python音频处理的示例详解

    准备工作: 首先,我们需要 import 几个工具包,一个是 python 标准库中的 wave 模块,用于音频处理操作,另外两个是 numpy 和 matplot,提供数据处理函数. 一:读取本地音 ...

  2. python数据库环境详解_python中MySQL数据库相关操作

    一 安装基本环境 1 简介 MySQL 基于TCP 协议之上的开发,但是网络连接后,传输的数据必须遵循MySQL的协议,封装好MySQL协议的包,就是驱动程序 MySQL 的驱动 MySQLDB 最有 ...

  3. python excel详解_Python - excel 详解

    Python读excel,2003用xlrd,2007和2010用openpyxl xlrd介绍:http://pypi.python.org/pypi/xlrd 转自:http://huaxia52 ...

  4. python多线程详解_Python多线程详解-Python-火龙果软件

    编辑推荐: 本文来自于博客,本文详细介绍了如何使用Python操作MySQL.使用Python操作Redis及项目实战. 操作MySQL 1)Windows中安装python和pycharm 2)ub ...

  5. python manager详解_Python通过Manager方式实现多个无关联进程共享数据

    Python官方文档 Python实现多进程间通信的方式有很多种,例如队列,管道等. 但是这些方式只适用于多个进程都是源于同一个父进程的情况. 如果多个进程不是源于同一个父进程,只能用共享内存,信号量 ...

  6. python变量详解_Python 变量详解[学习 Python 必备基础知识][看此一篇就够了]

    您的"关注"和"点赞",是信任,是认可,是支持,是动力...... 如意见相佐,可留言. 本人必将竭尽全力试图做到准确和全面,终其一生进行修改补充更新. 1 P ...

  7. python递归详解_python基于递归解决背包问题详解

    递归是个好东西,任何具有递归性质的问题通过函数递归调用会变得很简单.一个很复杂的问题,几行代码就能搞定. 最简单的递归问题:现有重量为weight的包,有若干重量分别为W1,W2.....Wn的物品, ...

  8. python递归详解_Python理解递归的方法总结

    递归 一个函数在执行过程中一次或多次调用其本身便是递归,就像是俄罗斯套娃一样,一个娃娃里包含另一个娃娃. 递归其实是程序设计语言学习过程中很快就会接触到的东西,但有关递归的理解可能还会有一些遗漏,下面 ...

  9. python多线程详解_python基础:python多线程详解

    前言 多线程类似于同时执行多个不同程序,多线程运行有如下优点:使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进 ...

最新文章

  1. Eclipse深度患者设置VSCode快捷键
  2. php xml写入数据库中,PHP读取xml并写入数据库示例
  3. PHP学习记录之会话处理(二)
  4. Java面向对象第二章课后习题
  5. MFC中将窗口最小化到系统托盘和还原
  6. Log4j介绍,log4j.properties配置详解
  7. Android Studio升级到2.3的编译问题解决办法
  8. 2020低压电工模拟考试系统及低压电工考试软件
  9. MAGIX Sound Forge Audio Studio v16.0.0.39 WiN 音频编辑软件
  10. 鹏业安装算量软件V8.0.0.92升级内容
  11. itext设置字体间距_微信公众号文章字体怎么修改?行间距、字间距一般设置多少?...
  12. java field 赋值_Java Field.set()向对象的这个Field属性设置新值value
  13. CF949 简要题解
  14. 显卡的指标有哪些方面_显卡的几个主要的性能指标有()
  15. html做成小程序,HTML-简单表单制作-表单制作-小程序表单制作
  16. 设计模式-----装饰模式.
  17. 马丁福勒《UML精粹》读书笔记_第四章
  18. ​ 斯科特·马特森​与MIMO发明者谈话节选
  19. 谷歌时代结束 - Google中国名称已经改回
  20. [DataAnalysis]关联分析

热门文章

  1. 教练,我想学设计之禅
  2. 解决hadoop namenode 无法启动
  3. [源码] Eoxplayer主框架流程
  4. Iphone 通过USB共享网络给Linux, IOS14+不能使用的解决方法
  5. ZED2跑ORB-SLAM3+双目相机、IMU联合标定+显卡驱动与cuda/cudnn安装
  6. 计算机文件预览取消,关闭视频文件预览,保留图片文件预览(windows7) -电脑资料...
  7. 对LOAM算法原理和代码的理解
  8. 深度学习评价指标系列——CDF累积分布函数(分布函数)
  9. C#自定义控件添加事件例程
  10. Blender基础技巧小结