# 重要对象及关键源码

# 运行时重要对象

在CPython虚拟机运行期间,比较重要的一些对象包括:

  1. Code Object​:表示Python源代码的编译结果,包括字节码指令、常量池、变量名等信息,是Python程序的执行单位。
  2. Frame Object​:表示函数调用时的栈帧,保存了函数的局部变量、参数、返回地址、当前行号等信息,是Python虚拟机执行代码时的基本单位。
  3. Function Object​:表示函数对象,保存了函数的代码、全局变量等信息,是一种可调用的对象。
  4. Module Object​:表示模块对象,保存了模块的全局变量、函数等信息。
  5. Type Object​:表示类型对象,是 Python 对象模型的基石之一,每个对象都有一个对应的类型对象,决定了对象所能够支持的操作和行为。类型对象本身也是 Python 对象,其类型是 type​。
  6. Class Object​:表示类对象,在 Python 中,每个类都有一个对应的类对象,类对象包含了类定义中的所有方法和属性。类对象也是一个 Python 对象,其类型是 type​。

这些对象之间的关系如下:

  • Code Object​对象是函数对象和模块对象的核心组成部分,描述了函数或模块的执行流程、变量名等信息。
  • Function Object​对象是一个可调用对象,与Code Object​对象关联,保存了函数的代码、全局变量等信息,同时可以通过函数名称访问。
  • Module Object​对象是一个全局作用域,保存了模块的全局变量、函数等信息,同时也是一个命名空间,可以通过模块名称访问。
  • Frame Object​对象保存了函数调用时的信息,包括函数的局部变量、参数、返回地址、当前行号等信息,同时也维护了函数调用栈的状态,是Python虚拟机执行代码时的基本单位,每个栈帧与一个Code Object​对象相关联,同时也可以访问上层栈帧的变量信息。
  • 类型对象是所有 Python 对象的基类,所有对象都有一个对应的类型对象,类型对象之间也可以存在继承关系;
  • 类对象是创建实例对象的工厂,实例对象包含了对应的类对象、属性和方法等;

# PyCodeObject

/* Bytecode object */
typedef struct {
    PyObject_HEAD
    int co_argcount;            /* 参数个数 #arguments, except *args */ 
    int co_kwonlyargcount;      /* 关键字参数个数 #keyword only arguments */
    int co_nlocals;             /* 局部变量个数 #local variables */
    int co_stacksize;           /* 执行代码所需栈空间 #entries needed for evaluation stack */
    int co_flags;               /* 标识 CO_..., see below */
    int co_firstlineno;         /* 代码块首行行号 first source line number */
    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 aren't used in either hash or comparisons, except for co_name,
       used in both. This is done to preserve the name and line number
       for tracebacks and debuggers; otherwise, constant de-duplication
       would collapse identical functions/lambdas defined on different lines.
    */
    Py_ssize_t *co_cell2arg;    /* Maps cell vars which are arguments. */
    PyObject *co_filename;      /* unicode (where it was loaded from) */
    PyObject *co_name;          /* unicode (name, for reference) */
    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 */
    /* Scratch space for extra data relating to the code object.
       Type is a void* to keep the format private in codeobject.c to force
       people to go through the proper APIs. */
    void *co_extra;
} PyCodeObject;

# PyEval_EvalFrameEx

PyEval_EvalFrameEx 函数最终调用 _PyEval_EvalFrameDefault 函数执行 frame 对象上的代码对象。虽然它体量巨大,超过 3 千行代码,逻辑却非常直白 —— 内部由无限 for 循环逐条遍历并处理字节码,每执行完一条字节码就自增 f_lasti 字段。