769344f9b07bc9a8e13b45e2979963a9
Python的编译:从py到pyc

之前的文章,我们大多数讲了Python文件一步步是如何进行词法分析、文法定义、语法分析以及具体的实现方式。

今天我们来谈谈Python编程中一个一直被人忽略的部分:Pyc

Python是不是解释型语言

从定义上来说,Python 确实属于一门解释型语言。但实质上Python只是定义了一套语言标准,具体实现并无固化。我们来看看目前几种主流的实现:

  • CPython是标准Python,也是其他Python编译器的参考实现。CPythonC编写,将Python源码编译成CPython字节码,由虚拟机解释执行。

  • Jython:这是PythonJava实现,相比于CPython,它与Java语言之间的互操作性要远远高于CPythonC语言之间的互操作性。

    Jython会将Python代码动态编译成Java字节码,然后在JVM上运行转换后的程序,这意味着此时Python程序与Java程序没有区别,只是源代码不一样。你甚至可以把Jython 脚本静态地编译为Java 字节码

  • PyPy,具有JIT,基于RPython使用纯Python开发的一个高性能Python解释器。

从上文不难看出,如果编译型语言的定义是:从源代码直接编译成机器可以执行的机器码。那Python确实只能称为解释型语言;而如果你认为从源代码到Python字节码直接的转换可以称为编译过程,那么这种称呼就见仁见智啦。

PyCodeObject与pyc

回到本专栏关注的CPython上,我们之前提及:CPythonC编写,将Python源码编译成CPython字节码。 那么这里的编译仅仅包含了字节码吗?字节码仅仅是程序执行的逻辑而已,那对应的另一部分:数据在哪呢? 由此,引出我们本文的两个重点,PyCodeObjectpyc

PyCodeObject

万物皆对象,Python的编译过程产生的输出也是对象,是PyCodeObject

如果你觉得无法理解,你可以对比iOS开发的Mach-O

PyCodeObject本身定义如下:

/* Bytecode object */
typedef struct {
    PyObject_HEAD
    int co_argcount;        /* #arguments, except *args */
    int co_nlocals;     /* #local variables */
    int co_stacksize;       /* #entries needed for evaluation stack */
    int co_flags;       /* CO_..., see below */
    PyObject *co_code;      /* instruction opcodes */
    PyObject *co_consts;    /* list (constants used) */
    PyObject *co_names;     /* list of strings (names used) */
    PyObject *co_varnames;  /* tuple of strings (local variable names) */
    PyObject *co_freevars;  /* tuple of strings (free variable names) */
    PyObject *co_cellvars;      /* tuple of strings (cell variable names) */
    /* The rest doesn't count for hash/cmp */
    PyObject *co_filename;  /* string (where it was loaded from) */
    PyObject *co_name;      /* string (name, for reference) */
    int co_firstlineno;     /* first source line number */
    PyObject *co_lnotab;    /* string (encoding addr<->lineno mapping) See
                   Objects/lnotab_notes.txt for details. */
    void *co_zombieframe;     /* for optimization only (see frameobject.c) */
    PyObject *co_weakreflist;   /* to support weakrefs to code objects */
} PyCodeObject;

其中co_code就是代码对应的字节码指令序列。,剩下的所有的值域解释如下:

名称 作用
co_argcount CodeBlock的位置参数个数
co_nlocals CodeBlock中的局部变量个数
co_stacksize 执行该CodeBlock需要的栈空间的大小
co_flags 没卵用
co_code 代码对应的字节码指令序列
co_consts CodeBlock中的所有常量
co_names CodeBlock中的字符串
co_varnames CodeBlock中的局部变量名字符串
co_freevars CodeBlock中的跟闭包相关的数据
co_cellvars CodeBlock中的嵌套函数所引用的局部变量名集合
co_filename CodeBlock所在的文件路径
co_name 类名、函数名、module名
co_firstlineno CodeBlock在文件中的起始行
co_lnotab Debug用的,用于查看当前指令序列字节码与源代码行数的对应关系
top Created with Sketch.