Python 2.7 源码 - pyc 的结构

栏目: Python · 发布时间: 6年前

内容简介:Python 2.7 源码 - pyc 的结构

Python 是一门解释性语言,源码执行需要经过:编译-字节码-虚拟机的步骤。本文就介绍一下 .py 文件编译后的 .pyc 文件结构。直接运行的代码不会生成 .pyc ,而 Python 的 import 机制会触发 .pyc 文件的生成。

magic number 和修改时间

我们在导入模块的源码中,能找到 .pyc 文件的蛛丝马迹:

// Python/import.c
static void
write_compiled_module(PyCodeObject *co, char *cpathname, struct stat *srcstat, time_t mtime)
{
    ...
    PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION);
    /* First write a 0 for mtime */
    PyMarshal_WriteLongToFile(0L, fp, Py_MARSHAL_VERSION);
    PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
    ...
}

可以看到, .pyc 文件包含三个主要内容:magic number,修改时间,和一个 PyCodeObject 对象。

magic number 和 Python 版本有关,magic number 不同能够防止低版本误执行高版本编译后的字节码。修改时间能让编译器决定是否重新编译源文件。

我们来读取 .pyc 的前八字节来验证一下我们的分析, func0.py 是测试脚本:

## func0.py
    f = open(fname, 'rb')
    magic = f.read(4)
    moddate = f.read(4)
    modtime = time.asctime(time.localtime(struct.unpack('I', moddate)[0]))
    print 'magic number: %s' % (magic.encode('hex'))
    print 'moddate %s (%s)' % (moddate.encode('hex'), modtime)

被测试源文件:

# test.py
def add(a):
    a=1
    print a

使用以下强制编译成 .pyc

python -m py_compile test.py

测试结果:

$ python func0.py test.pyc
magic number: 03f30d0a
moddate 5e9a4b5a (Tue Jan  2 22:42:38 2018)

PyCodeObject 对象

PyCodeObject 是一段代码块编译的直接结果。换句话说,一个作用域,对应一个代码,最终对应一个编译后的 PyCodeObject

首先看一下 PyCodeObject 的结构:

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_consts 之中。

我们写个简单的脚本,展现这种嵌套的结构:

# func0.py
...
def show_code(code, indent=''):
    old_indent = indent  
    print "%s<code>" % indent  
    indent += '   '  
    show_hex("bytecode", code.co_code, indent=indent)  
    print "%s<filename> %r</filename>" % (indent, code.co_filename)  
    print "%s<consts>" % indent  
    for const in code.co_consts:  
        if type(const) == types.CodeType:  
            show_code(const, indent+'   ')  
        else:  
            print "   %s%r" % (indent, const)  
    print "%s</consts>" % indent  
    print "%s</code>" % old_indent

被测试的源文件:

# test.py
def add(a):
    a=1
    print a

测试步骤:

exs $ python func0.py test.pyc
<code>
   <bytecode> 6400008400005a000064010053</bytecode>
   <filename> 'test.py'</filename>
   <consts>
      <code>
         <bytecode> 6401007d00007c0000474864000053</bytecode>
         <filename> 'test.py'</filename>
         <consts>
            None
         </consts>
      </code>
      None
   </consts>
</code>

可以看到,函数 add 作为全局作用域的中一个子作用域,在编译结果中,是以常量形式存在于全局作用域的 PyCodeObject 中。

查看字节码

Python 提供了 dis 模块,其中的 disassemble() 函数可以反编译 PyCodeObject 对象,以可读的形式展现出来。

我们修改 func0.py ,将字节码对应的指令打印出来,增加下述代码:

...
show_hex("bytecode", code.co_code, indent=indent)  
print "%s<dis>" % indent  
dis.disassemble(code)  
print "%s</dis>" % indent  
...

测试结果:

$ python func0.py test.pyc
<code>
   <bytecode> 6400008400005a000064010053</bytecode>
   <dis>
  3           0 LOAD_CONST               0 (<code object add at 0x109aa1c30, file "test.py", line 3>)
              3 MAKE_FUNCTION            0
              6 STORE_NAME               0 (add)
              9 LOAD_CONST               1 (None)
             12 RETURN_VALUE
   </dis>
   <filename> 'test.py'</filename>
   <consts>
      <code>
         <bytecode> 6401007d00007c0000474864000053</bytecode>
         <dis>
  4           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (a)

  5           6 LOAD_FAST                0 (a)
              9 PRINT_ITEM
             10 PRINT_NEWLINE
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE
         </dis>
         <filename> 'test.py'</filename>
         <consts>
            None
            1
         </consts>
      </code>
      None
   </consts>
</code>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

离心力:互联网历史与数字化未来

离心力:互联网历史与数字化未来

[英] 乔尼·赖安(Johnny Ryan) / 段铁铮 / 译言·东西文库/电子工业出版社 / 2018-2-1 / 68.00元

★一部详实、严谨的互联网史著作; ★哈佛、斯坦福等高校学生必读书目; ★《互联网的未来》作者乔纳森·L. 齐特雷恩,《独立报》《爱尔兰时报》等知名作者和国外媒体联合推荐。 【内容简介】 虽然互联网从诞生至今,不过是五六十年,但我们已然有必要整理其丰富的历史。未来的数字世界不仅取决于我 们的设想,也取决于它的发展历程,以及互联网伟大先驱们的理想和信念。 本书作者乔尼· ......一起来看看 《离心力:互联网历史与数字化未来》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具