本文为senlie原创。转载请保留此地址:http://blog.csdn.net/zhengsenlie

1.python的运行过程

1)对python源码进行编译。产生字节码

2)将编译结果交给python虚拟机。由虚拟机依照顺序一条一条地运行字节码,产生运行结果







2.Python编译器的编译结果——PyCodeObject对象

Python编译器的编译结果中包括了字符串、常量值、字节码等在源码中出现的一切实用的静态信息。

在Python执行期间,这些静态信息被PyCodeObject对象中

在Python执行结束后。这些信息会被存储在pyc文件里

PyCodeObject对象和pyc文件是Python对源文件编译结果的两种不同存在形式





3.Python源代码中的PyCodeObject

  1. /* Bytecode object */
  2. typedef struct {
  3. PyObject_HEAD
  4. int co_argcount; /* 位置參数个数*/
  5. int co_nlocals; /* 局部变量个数,包含位置參数个数*/
  6. int co_stacksize; /* 须要的栈空间 */
  7. int co_flags; /* CO_..., see below */
  8. PyObject *co_code; /* 字节码指令序列,以PyStringObject形式存在 */
  9. PyObject *co_consts; /* PyTupleObject对象,保存全部的常量 */
  10. PyObject *co_names; /* PyTupleObject对象。保存全部符号 */
  11. PyObject *co_varnames; /* 局部变量名集合 */
  12. PyObject *co_freevars; /* 实现闭包须要用到的东西 */
  13. PyObject *co_cellvars; /* 内部嵌套函数所引用的局部变量名集合 */
  14. /* The rest doesn't count for hash/cmp */
  15. PyObject *co_filename; /* Code Block所相应的.py文件的完整路径 */
  16. PyObject *co_name; /* Code Block的名字,一般是函数名或类名 */
  17. int co_firstlineno; /* Code Block所相应的.py文件的起始行 */
  18. PyObject *co_lnotab; /* 字节码指令与.py文件里source code行号的相应关系。以PyStringObject的等式存在 */
  19. void *co_zombieframe; /* for optimization only (see frameobject.c) */
  20. PyObject *co_weakreflist; /* to support weakrefs to code objects */
  21. } PyCodeObject;

Code Block:当进入一个新的名字空间,或者说作用域的时候,就算是进入了一个新的Code Block:当进入一个新的名字空间。或者说作用域的时候,就算是进入了一个新的

Code Block。一个 Code Block相应一个 PyCodeObject

名字空间是符号的上下文环境。名字空间链是多个名字空间嵌套在一起。

在Python中,module、类、函数都相应着一个独立的名字空间

  1. #会产生三个 PyCodeObject。分别相应整个文件。class A和 def Fun
  2. class A:
  3. pass
  4. def Fun():
  5. pass
  6. a = A()
  7. Fun()

3.pyc文件

pyc文件里包括了三部分独立的信息:

Python的magic number --> 保证Python的兼容性

pyc文件的创建时间 --> 能够使Python自己主动将pyc文件与最新的py文件进行同步

PyCodeObject对象 





将内存中的PyCodeObject对象写入到pyc文件,须要下面几个函数

w_byte

w_long

w_string





PyMarshal_WriteObjectToFile会调用w_object,w_object会遍历 PyCodeObject中的全部域,

将这些域依次写入。

写入终于归结为两种形式:对数值的写入和对字符串的写入

  1. static void w_object(PyObject *v, WFILE *p)
  2. {
  3. //……
  4. else if (PyCode_Check(v))
  5. {
  6. PyCodeObject *co = (PyCodeObject *)v;
  7. w_byte(TYPE_CODE, p);
  8. w_long(co->co_argcount, p);
  9. w_long(co->co_nlocals, p);
  10. w_long(co->co_stacksize, p);
  11. w_long(co->co_flags, p);
  12. w_object(co->co_code, p);
  13. w_object(co->co_consts, p);
  14. w_object(co->co_names, p);
  15. w_object(co->co_varnames, p);
  16. w_object(co->co_freevars, p);
  17. w_object(co->co_cellvars, p);
  18. w_object(co->co_filename, p);
  19. w_object(co->co_name, p);
  20. w_long(co->co_firstlineno, p);
  21. w_object(co->co_lnotab, p);
  22. }
  23. //……
  24. }

w_object在写入对象之前会先写入TYPE_LIST、TYPE_CODE或者TYPE_INT标识,它们对

pyc文件的再次载入具有至关关键的数据。Python在pyc文件里发现这种标识,则预示着

一个对象的结束,新对象的開始。并且也知道了对象的类型。

4.向pyc文件里写入字符串

写入过程中的结构体WFILE

  1. typedef struct {
  2. FILE *fp;
  3. PyObject *strings; //在写入时指向dict,在读出时指向list
  4. } WFILE;

WFILE中的strings在marshal的时候指向一个PyDictObject对象(PyStringObject,PyIntObject)

  1. //w_object对于字符串的处理
  2. else if (PyString_CheckExact(v)) {
  3. if (p->strings && PyString_CHECK_INTERNED(v)) {
  4. //[1]:获得 PyStringObject对象在strings中的序号
  5. PyObject *o = PyDict_GetItem(p->strings, v);
  6. //[2]:intern字符串的非首次写入
  7. if (o) {
  8. long w = PyInt_AsLong(o);
  9. w_byte(TYPE_STRINGREF, p);
  10. w_long(w, p);
  11. goto exit;
  12. }
  13. //[3]:intern字符串的首次写入
  14. else {
  15. int ok;
  16. o = PyInt_FromSsize_t(PyDict_Size(p->strings));
  17. ok = o &&
  18. PyDict_SetItem(p->strings, v, o) >= 0;
  19. Py_XDECREF(o);
  20. if (!ok) {
  21. p->depth--;
  22. p->error = WFERR_UNMARSHALLABLE;
  23. return;
  24. }
  25. w_byte(TYPE_INTERNED, p);
  26. }
  27. }
  28. //[4]:写入普通字符串
  29. else {
  30. w_byte(TYPE_STRING, p);
  31. }
  32. //写入字符串
  33. w_pstring(PyBytes_AS_STRING(v), PyString_GET_SIZE(v), p);
  34. }
  35. //...

怎么确定一个字符串要不要intern?

写入pyc文件时,strings是dict类型



从pyc文件里读取的时候,strings是list类型







5.一个PyCodeObject,多个PyCodeObject

PyCodeObject中的co_consts是嵌套的PyCodeObject的藏身之处





6.Python的字节码

104条字节码

  1. STOP_CODE()
  2. Indicates end-of-code to the compiler, not used by the interpreter.
  3. NOP()
  4. Do nothing code. Used as a placeholder by the bytecode optimizer.
  5. POP_TOP()
  6. Removes the top-of-stack (TOS) item.
  7. ROT_TWO()
  8. Swaps the two top-most stack items.
  9. ROT_THREE()
  10. Lifts second and third stack item one position up, moves top down to position three.
  11. ROT_FOUR()
  12. Lifts second, third and forth stack item one position up, moves top down to position four.
  13. //...

《python源代码剖析》笔记 Python的编译结果的更多相关文章

  1. Python源代码剖析笔记3-Python运行原理初探

    Python源代码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源代码剖析笔记,然而慢慢觉得没有从一个宏观 ...

  2. 《python源代码剖析》笔记 Python虚拟机框架

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令 ...

  3. 《python源代码剖析》笔记 python虚拟机中的函数机制

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.Python虚拟机在运行函数调用时会动态地创建新的 PyFrameObject对象, 这 ...

  4. 《python源代码剖析》笔记 python环境初始化

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/zhsenl/article/details/33747209 本文为senlie原创.转载请保留此地 ...

  5. 《python源代码剖析》笔记 python中的List对象

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyListObject对象 --> 变长可变对象,可看作vector<Py ...

  6. 《python源代码剖析》笔记 python中的Dict对象

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyDictObject对象 -->  C++ STL中的map是基于RB-tre ...

  7. Python知识点入门笔记——Python文件操作、异常处理及random模块使用

    文件是存储在外部介质的数据集合,通常可以长久保存,前提是介质不易损坏 Python的绝对路径写法: E:\\编程学习资料\\爬取某社区高清无码大图.py E:/编程学习资料/爬取某社区高清无码大图.p ...

  8. Python知识点入门笔记——Python的基本数据类型

    Python的数字分为4种类型:整数(int).浮点数(float).布尔值(bool).复数(complex). type()函数可以知道数据的类型,如type(233)是int型,type(233 ...

  9. 流畅的python第九章笔记 python风格的python

    9.1对象表示形式 __repr__和__str__这两个方法都是用于显示的,__str__是面向用户的,而__repr__面向程序员. 我们打印下面的A是默认输出这个对象的类型,我们对B进行了修改_ ...

随机推荐

  1. [转]ARM平台下独占访问指令LDREX和STREX

    参考:ARM平台下独占访问指令LDREX和STREX的原理与使用详解 全文转载如下: 为了实现线程间同步,一般都要在执行关键代码段之前加互斥(Mutex)锁,且在执行完关键代码段之后解锁.为了实现所谓 ...

  2. docker+Battery Historian 环境搭建(电量分析)

    docker 安装(windows) 1.  下载 https://docs.docker.com/docker-for-windows/install/  和 安装和添加环境变量(...) 2. 安 ...

  3. python-selenium使用send_keys()方法写中文报错的解决方法

    问题描述: 自动化操作页面,输入中文姓名: # coding=utf-8 url = "http://dealer.bitauto.com/50002218/zuidijia/" ...

  4. Leetcode 322.零钱兑换

    零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: co ...

  5. 九度oj 题目1534:数组中第K小的数字

    题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C. 譬如A为[1,2],B为[3,4].那么由A和B中的元素两两相加得到的数组C为[4,5,5,6]. 现在给你数组A和B ...

  6. Azure Storage Blob文件名区分大小写

    最近在使用Azure Storage的时候发现Storage的命名是区分大小写的,导致我们系统在更新图片的时候有时候更新不上,最终通过判断处理文件名解决. 因此我们在使用Storage需要注意一下文件 ...

  7. 让Sublime Text成为静态WEB服务器:SublimeServer

    如果你使用Sublime Text作为你的编辑器,那么在进行HTML和Java开发的时候有一个很有用的功能,帮你完成前端的联调测试,那就是Sublime Text的服务器插件:SublimeServe ...

  8. IntelliJ IDEA 代码提示快捷键

    1.写代码时用Alt-Insert(Code|Generate…)可以创建类里面任何字段的getter与setter方法. mac版 是ctrl+enter 2.CodeCompletion(代码完成 ...

  9. HDU——2612Find a way(多起点多终点BFS)

    Find a way Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  10. [UOJ#122][NOI2013]树的计数

    [UOJ#122][NOI2013]树的计数 试题描述 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的 DFS 序以及 BFS 序.两棵不同的树的 DFS 序 ...