上一节:Python之code对象与pyc文件(一)

创建pyc文件的具体过程

前面我们提到,Python在通过import或from xxx import xxx时会对module进行动态加载,如果没有找到相应的pyc或dll文件,就会在py文件的基础上创建pyc文件,之前说过,pyc文件中保存的是PyCodeObject对象,那么我们就要搞清楚,PyCodeObject是如何写入到pyc文件中的

import.c

static void
write_compiled_module(PyCodeObject *co, char *cpathname, time_t mtime)
{
FILE *fp;
//排他性打开文件
fp = open_exclusive(cpathname);
//<1>写入Python的magic number
PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION);
//<2>写入PyCodeObject对象
PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
//<3>写入时间信息
PyMarshal_WriteLongToFile((long)mtime, fp, Py_MARSHAL_VERSION);
fflush(fp);
fclose(fp);
}

  

write_compiled_module中的代码略有缩减,我们只保留最需要关注的部分。可以发现,一个pyc文件中实际上包含了3个部分独立的信息,Python中的magic number、PyCodeObject对象以及创建pyc文件的时间

在<1>处,Python会将pyc_magic这个值写入到文件的开头,pyc_magic是一个整数值,不同版本的Python的都会定义不同的magic number,在Python加载一个pyc文件时,会先检查pyc文件中的pyc_magic与当前Python版本所对应的pyc_magic是否一致,避免了Python2.5加载Python1.5编译出来的pyc文件。之所以要做这个检查,是因为不同版本的Python的字节码指令都可能有做不同的变动,一些旧的指令会被新的指令所代替,甚至还会加入新的指令,这都是导致Python不兼容的问题

在import.c中,可以在源代码的注释里找到Python1.5到Python2.5所有版本的magic number,我们可以看一下Python2.5定义的magic number:

import.c

#define MAGIC (62131 | ((long)'\r'<<16) | ((long)'\n'<<24))
static long pyc_magic = MAGIC;

  

在pyc中,在<3>处完成了向pyc文件写入时间信息的动作。在pyc文件中包含时间信息可以使Python对比pyc和最新的py文件进行对比,如果发现pyc的生成时间早于py文件的修改时间,则代表py文件被修改过,会重新编译pyc文件

在上面代码的<2>处,Python会调用PyMarshal_WriteObjectToFile方法,将内存中的PyCodeObject对象写入pyc文件中,在write_compiled_module中,向pyc文件写入数据的动作最后会集中到下面所示的几个函数中

现在,我们来看一下PyMarshal_WriteObjectToFile这个方法 

marshal.c 

void PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp, int version)
{
WFILE wf;
wf.fp = fp;
wf.error = 0;
wf.depth = 0;
wf.strings = (version > 0) ? PyDict_New() : NULL;
wf.version = version;
w_object(x, &wf);
Py_XDECREF(wf.strings);
}

  

PyMarshal_WriteObjectToFile这个方法中调用w_object这个方法,将对象真正写入到文件中

marshal.c

static void w_object(PyObject *v, WFILE *p)
{
……
else if (PyTuple_Check(v)) {
……
}
else if (PyList_Check(v)) {
……
}
else if (PyDict_Check(v)) {
……
}
……
else if (PyCode_Check(v)) {
PyCodeObject *co = (PyCodeObject *)v;
w_byte(TYPE_CODE, p);
w_long(co->co_argcount, p);
w_long(co->co_nlocals, p);
w_long(co->co_stacksize, p);
w_long(co->co_flags, p);
w_object(co->co_code, p);
w_object(co->co_consts, p);
w_object(co->co_names, p);
w_object(co->co_varnames, p);
w_object(co->co_freevars, p);
w_object(co->co_cellvars, p);
w_object(co->co_filename, p);
w_object(co->co_name, p);
w_long(co->co_firstlineno, p);
w_object(co->co_lnotab, p);
}
……
}

  

从上面的代码我们可以看到,在w_object中,会遍历PyCodeObject中的各个域,将这些域一次写入。

当w_object面对一个PyListObject对象时,会有什么动作?

marshal.c

else if (PyList_Check(v)) {
w_byte(TYPE_LIST, p);
n = PyList_GET_SIZE(v);
w_long((long)n, p);
for (i = 0; i < n; i++) {
w_object(PyList_GET_ITEM(v, i), p);
}
}

  

如同前面对PyCodeObject一样,w_object还是遍历,将PyListObject对象中的每一个元素一次写入到pyc文件中

我们稍微浏览一遍w_object这个方法,会发现在写入任何一个对象之前,,都会先写入一个TYPE_LIST或者TYPE_CODE这样的类型标识,这些标识对于pyc文件再次加载具有至关重要的作用。如果我们仅仅是将对象中数值和字符串信息写入到pyc文件,如果没有对应的类型信息,我们很难将这些数值或者字符串恢复到以前在内存中所对应的对象。而在Python加载pyc文件时,发现了类型信息,就预示着上一个对象结束,新的对象开始,而且也知道新对象是什么类型的对象。这样,当Python加载pyc文件时,加载器才能知道在什么时候应该进行什么样的加载操作

类型标识在Python中的定义:

marshal.c

#define TYPE_NULL		'0'
#define TYPE_NONE 'N'
#define TYPE_FALSE 'F'
#define TYPE_TRUE 'T'
#define TYPE_STOPITER 'S'
#define TYPE_ELLIPSIS '.'
#define TYPE_INT 'i'
#define TYPE_INT64 'I'
#define TYPE_FLOAT 'f'
#define TYPE_BINARY_FLOAT 'g'
#define TYPE_COMPLEX 'x'
#define TYPE_BINARY_COMPLEX 'y'
#define TYPE_LONG 'l'
#define TYPE_STRING 's'
#define TYPE_INTERNED 't'
#define TYPE_STRINGREF 'R'
#define TYPE_TUPLE '('
#define TYPE_LIST '['
#define TYPE_DICT '{'
#define TYPE_CODE 'c'
#define TYPE_UNICODE 'u'
#define TYPE_UNKNOWN '?'
#define TYPE_SET '<'
#define TYPE_FROZENSET '>'

  

Python之code对象与pyc文件(二)的更多相关文章

  1. Python之code对象与pyc文件(三)

    上一节:Python之code对象与pyc文件(二) 向pyc写入字符串 在了解Python如何将字符串写入到pyc文件的机制之前,我们先来了解一下结构体WFILE: marshal.c typede ...

  2. Python之code对象与pyc文件(一)

    Python程序的执行过程 我们都知道,C语言在执行之前需要将源代码编译成可执行的二进制文件,也就是将源代码翻译成机器代码,这种二进制文件一旦生成,即可用于执行.但是,Python是否一样呢?或许很多 ...

  3. 《python解释器源码剖析》第8章--python的字节码与pyc文件

    8.0 序 我们日常会写各种各样的python脚本,在运行的时候只需要输入python xxx.py程序就执行了.那么问题就来了,一个py文件是如何被python变成一系列的机器指令并执行的呢? 8. ...

  4. 关于python包,模块,.pyc文件和文件导入理解

    参考文献 一.包 包是一个文件夹,用来存放模块和子包. 包里一般会有一个__init__.py的文件(也可以没有). 包里会有一个__pycache__文件夹,存放.py文件经解释器解释后的中间字节码 ...

  5. Python编程时.py与.pyc文件的介绍

    Python的程序中,是把原始程序代码放在.py文件里,而Python会在执行.py文件的时候.将.py形式的程序编译成中间式文件(byte-compiled)的.pyc文件,这么做的目的就是为了加快 ...

  6. python运行时禁止生成pyc文件

    方法 在环境变量文件~/.bashrc中添加 export PYTHONDONTWRITEBYTECODE=False source ~/.bashrc加载即可 如何从项目中删除所有.pyc文件 fi ...

  7. .pyc文件的结构体PyCodeObject

    python执行程序时生成的pyc文件里面是,PyCodeObject 的结构体构成,每个命名空间(函数名.import模块等)都会形成一个core block,一个python程序的所有命名空间生成 ...

  8. 删除项目开发中的.pyc文件

    在实际开发中python会自动生成很多pyc文件,但是这些pyc文件是不需要我们追踪的,删除了对项目也没有影响,下面是删除pyc文件的方法. Linux或Mac系统 find /tmp -name & ...

  9. Python逆向(二)—— pyc文件结构分析

    一.前言 上一节我们知道了pyc文件是python在编译过程中出现的主要中间过程文件.pyc文件是二进制的,可以由python虚拟机直接执行的程序.分析pyc文件的文件结构对于实现python编译与反 ...

随机推荐

  1. Jquery使用ajax参数详解

    记录一下  Jquery使用ajax(post.get及参数详解) 1.get: $.ajax({ type: "GET", url: baseUrl + "Showco ...

  2. android应用开发全程实录-你有多熟悉listview?

    今天给大家带来<android应用开发全程实录>中关于listview和adatper中的部分.包括listview的基本使用,listview的优化等. 我们经常会在应用程序中使用列表的 ...

  3. 实例——省市区三级联动 & 还可以输入字符统计

    1 省市区三级联动 html代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8" ...

  4. windows下vue-cli及webpack 构建网站(一)环境安装

    1.安装Node.js,node.js安装包及源码下载地址为:https://nodejs.org/en/download/. 2.安装npm,由于新版的nodejs已经集成了npm,所以之前npm也 ...

  5. css hack 浏览器携带自身特有的属性 (二)

    css hack 浏览器携带自身特有的属性,才是我们真正要解决的css 兼容问题. 这里只是分享思路. 举例子: 1 outline,尤其是一些 自带继承特性的属性.这里指的是 隐性的inherite ...

  6. Dictionary(支持 XML 序列化),注意C#中原生的Dictionary类是无法进行Xml序列化的

    /// <summary> /// Dictionary(支持 XML 序列化) /// </summary> /// <typeparam name="TKe ...

  7. dstat参数选项

    Usage: dstat [-afv] [options..] [delay [count]]Versatile tool for generating system resource statist ...

  8. 使用com.sun.imageio.plugins.png.PNGMetadata读取图片的元数据

    所谓图片元数据,就是除了我们肉眼看到的图片内容外,隐藏在这些内容背后的一些技术数据. 本文介绍如何使用Java代码将一张图片的隐藏信息读取出来. 首先不需要下载任何额外的Java库,用JDK自带的库就 ...

  9. UVA 1152 4 Values Whose Sum is Zero 和为0的4个值 (中途相遇)

    摘要:中途相遇.对比map,快排+二分查找,Hash效率. n是4000的级别,直接O(n^4)肯定超,所以中途相遇法,O(n^2)的时间枚举其中两个的和,O(n^2)的时间枚举其他两个的和的相反数, ...

  10. [Tracking] KCF + KalmanFilter目标跟踪

    基于KCF和MobileNet V2以及KalmanFilter的摄像头监测系统 简介 这是一次作业.Tracking这一块落后Detection很多年了,一般认为Detection做好了,那么只要能 ...