专业汉语词典知识平台,分享汉字词语知识、历史文学知识解答!

励北网
励北网

pyc文件是什么,python中pyc文件详解

来源:小易整编  作者:小易  发布时间:2023-02-25 06:55
摘要:pyc文件是什么,python中pyc文件详解pyc文件的触发上一篇文章我们介绍了字节码,当时提到,py文件在执行的时候会先被编译成PyCodeObject对象,并且该对象还会被保存到pyc文件中。但不幸的是,事实并不总是这样,有时当我们运...
from importlib.util import MAGIC_NUMBER
import struct
import time
from types import CodeType
import marshal
from opcode import opmap
HEADER = MAGIC_NUMBER + b"\x00" * 4
# 时间随便写
HEADER += struct.pack("<I", int(time.time()))
# 大小随便写
HEADER += struct.pack("<I", 30)
# 构建 PyCodeObject
code = CodeType(
    0,                # co_argcount
    0,                # co_posonlyargcount
    0,                # co_kwonlyargcount
    3,                # co_nlocals
    1,                # co_stacksize
    0,                # co_flags
    bytes([
        # a = 1 分为两步
        # 第一步:先通过 LOAD_CONST 将常量加载进来
        # 因此指令是 LOAD_CONST,然后参数是 0
        # 表示加载常量池中索引为 0 的常量
        opmap["LOAD_CONST"], 0,
        # 第二步:通过 STORE_NAME 将常量和符号绑定起来
        # 参数是 0,表示和符号表中索引为 0 的符号进行绑定
        opmap["STORE_NAME"], 0,
        # b = 2
        opmap["LOAD_CONST"], 1,
        opmap["STORE_NAME"], 1,
        # c = 3
        opmap["LOAD_CONST"], 2,
        opmap["STORE_NAME"], 2,
        # 结尾要 LOAD 一个 None,然后返回
        opmap["LOAD_CONST"], 3,
        opmap["RETURN_VALUE"]
    ]),               # co_code
    (1, 2, 3, None),  # co_consts
    ("a", "b", "c"),  # co_names
    (),               # co_varnames
    "build_pyc.py",   # co_filename
    "<module>",       # co_name
    1,                # co_firstlineno
    b"",              # co_lnotab
    (),               # freevars
    ()                # cellvars
)
# pyc 文件内容
pyc_content = HEADER + marshal.dumps(code)
# 生成 pyc 文件
with open("build_pyc.pyc", "wb") as f:
    f.write(pyc_content)
# 然后加载生成的 pyc 文件
from importlib.machinery import SourcelessFileLoader
mod = SourcelessFileLoader(
    "build_pyc", "build_pyc.pyc"
).load_module()
print(mod)  # <module 'build_pyc' from 'build_pyc.pyc'>
print(mod.a)  # 1
print(mod.b)  # 2
print(mod.c)  # 3

怎么样,是不是很有趣呢?

pyc 文件的写入

下面通过源码来查看 pyc 文件的写入过程,既然要写入,那么肯定要有文件句柄。

//位置:Python/marshal.c
//FILE是 C 自带的文件句柄
//可以把WFILE看成是FILE的包装
typedef struct {
    FILE *fp;  //文件句柄
    //下面的字段在写入信息的时候会看到
    int error;  
    int depth;
    PyObject *str;
    char *ptr;
    char *end;
    char *buf;
    _Py_hashtable_t *hashtable;
    int version;
} WFILE;

首先是写入 magic number、创建时间和文件大小,它们会调用 PyMarshal_WriteLongToFile 函数进行写入:

void
PyMarshal_WriteLongToFile(long x, FILE *fp, int version)
{  
    //magic number、创建时间和文件大小,只是一个整数
    //在写入的时候,使用char [4]来保存
    char buf[4];
    //声明一个WFILE类型变量wf
    WFILE wf;
    //内存初始化
    memset(&wf, 0, sizeof(wf));
    //初始化内部成员
    wf.fp = fp;
    wf.ptr = wf.buf = buf;
    wf.end = wf.ptr + sizeof(buf);
    wf.error = WFERR_OK;
    wf.version = version;
    //调用w_long将x、也就是版本信息或者时间写到wf里面去
    w_long(x, &wf);
    //刷到磁盘上
    w_flush(&wf);
}

所以该函数只是初始化了一个 WFILE 对象,真正写入则是调用的 w_long。

static void
w_long(long x, WFILE *p)
{
    w_byte((char)( x      & 0xff), p);
    w_byte((char)((x>> 8) & 0xff), p);
    w_byte((char)((x>>16) & 0xff), p);
    w_byte((char)((x>>24) & 0xff), p);
}

w_long 则是调用 w_byte 将 x 逐个字节地写到文件里面去。

而写入 PyCodeObject 对象则是调用 PyMarshal_WriteObjectToFile,它实际又会调用 w_object 进行写入。

static void
w_object(PyObject *v, WFILE *p)
{
    char flag = '\0';
    p->depth++;
    if (p->depth > MAX_MARSHAL_STACK_DEPTH) {
        p->error = WFERR_NESTEDTOODEEP;
    }
    else if (v == NULL) {
        w_byte(TYPE_NULL, p);
    }
    else if (v == Py_None) {
        w_byte(TYPE_NONE, p);
    }
    else if (v == PyExc_StopIteration) {
        w_byte(TYPE_STOPITER, p);
    }
    else if (v == Py_Ellipsis) {
        w_byte(TYPE_ELLIPSIS, p);
    }
    else if (v == Py_False) {
        w_byte(TYPE_FALSE, p);
    }
    else if (v == Py_True) {
        w_byte(TYPE_TRUE, p);
    }
    else if (!w_ref(v, &flag, p))
        w_complex_object(v, flag, p);
    p->depth--;
}

可以看到本质上还是调用了 w_byte,但这仅仅是一些特殊的对象。如果是列表、字典之类的数据,那么会调用 w_complex_object,也就是代码中的最后一个 else if 分支。

w_complex_object 这个函数的源代码很长,我们看一下整体结构,具体逻辑就不贴了,后面会单独截取一部分进行分析。

static void
w_complex_object(PyObject *v, char flag, WFILE *p)
{
    Py_ssize_t i, n;
    //如果是整数的话,执行整数的写入逻辑
    if (PyLong_CheckExact(v)) {
        //......
    }
    //如果是浮点数的话,执行浮点数的写入逻辑
    else if (PyFloat_CheckExact(v)) {
        if (p->version > 1) {
            //......
        }
        else {
            //......
        }
    }
    //如果是复数的话,执行复数的写入逻辑
    else if (PyComplex_CheckExact(v)) {
        if (p->version > 1) {
            //......
        }
        else {
            //......
        }
    }
    //如果是字节序列的话,执行字节序列的写入逻辑
    else if (PyBytes_CheckExact(v)) {
        //......
    }
    //如果是字符串的话,执行字符串的写入逻辑
    else if (PyUnicode_CheckExact(v)) {
        if (p->version >= 4 && PyUnicode_IS_ASCII(v)) {
              //......
            }
            else {
                //......
            }
        }
        else {
            //......
        }
    }
    //如果是元组的话,执行元组的写入逻辑
    else if (PyTuple_CheckExact(v)) {
       //......
    }
    //如果是列表的话,执行列表的写入逻辑
    else if (PyList_CheckExact(v)) {
        //......
    }
    //如果是字典的话,执行字典的写入逻辑
    else if (PyDict_CheckExact(v)) {
        //......
    }
    //如果是集合的话,执行集合的写入逻辑
    else if (PyAnySet_CheckExact(v)) {
        //......
    }
    //如果是PyCodeObject对象的话
    //执行PyCodeObject对象的写入逻辑
    else if (PyCode_Check(v)) {
        //......
    }
    //如果是Buffer的话,执行Buffer的写入逻辑
    else if (PyObject_CheckBuffer(v)) {
        //......
    }
    else {
        W_TYPE(TYPE_UNKNOWN, p);
        p->error = WFERR_UNMARSHALLABLE;
    }
}

源代码虽然长,但是逻辑非常单纯,就是对不同的对象、执行不同的写动作,然而其最终目


本文地址:百科问答频道 https://www.neebe.cn/wenda/903398_2.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!


百科问答
小编:小易整编
相关文章相关阅读
  • Python前端是什么意思?

    Python前端是什么意思?

    Python前端是指利用Python进行网页开发的过程,是采用Python语言构建网页的技术。Python具有优秀的数据分析和可视化性能,具有更多分析和可视化专业应用,因此它也被开发者们当作WEB开发领域的第一选择语言来使用。Python...

  • MicroPython是什么

    MicroPython是什么

    MicroPython是Python3编程语言的一个完整软件实现,用C语言编写,被优化于运行在微控制器之上。运行在微控制器硬件之上的完全的Python编译器和运行时系统。提供给用户一个交互式提示符来立即执行所支持的命令。Mi...

  • Python语言有哪些特点

    Python语言有哪些特点

    简单易学、面向对象、可移植性、解释性、高级语言、可扩展性、丰富的库、规范的代码、开放源码软件。Python是一种面向对象的、解释型的、通用的、开源的脚本编程语言,它之所以非常流行为主要有三点原因:Python简单易用,学习成本低,看起来...

  • Python是什么意思

    Python是什么意思

    Python是计算机程序设计语言,应用于Web和Internet开发、人工智能、教育、软件开发等领域。Python的设计风格简单明确、清晰优雅,采用的都是最成熟的优化技术,非常受用户欢迎。Python是计算机程序设计语言,应用...

  • Python的作用是什么

    Python的作用是什么

    Python是一种广泛使用的高级编程语言,属于通用型编程语言,是完全面向对象的语言。函数、模块、数字、字符串都是对象。经常被用于Web开发、GUI开发、操作系统、科学计算等应用范围。Python是一种广泛使用的高级编程语言,属于通用型编程...

  • Python可以应用在哪些领域

    Python可以应用在哪些领域

    常规软件开发、科学计、自动化运维、云计算、WEB开发、网络爬虫、数据分析、人工智能,Python可以在这些领域都可应用到。Python是一门解释型、面向对象、带有动态语义的高级程序设计语言。Python具有强大而丰富的类库,也经常被别人...

  • n的阶乘公式,Python实例计算数字n的阶乘

    n的阶乘公式,Python实例计算数字n的阶乘

    n的阶乘公式,Python实例计算数字n的阶乘前言此实例题目来自网络,很基础的练手项目,根据这个实例主要总结了四种方法,希望对你有帮助。本文章的目的是通过实例学习python,适合初学者观看,但是需要有一定的python基础。编程环境pyt...

  • ini是什么,Python中ini详解

    ini是什么,Python中ini详解

    ini是什么,Python中ini详解INI简介INI即Initialize初始化之意,早期是在Windows上配置文件的存储格式。INI文件的写法通俗易懂,往往比较简单,通常由节(Section)、键(key)和值(value)组成,se...

  • 周排行
  • 月排行
  • 年排行

精彩推荐