第0章 准备工作
0.1 Python总体架构
0.1.1 左边(python提供的模块, 库和用户自定义模块)
概念 说明 模块 单个文件的形式 库 应该指包和模块两种形式(这里暂时不清楚) 自定义模块 略
0.1.2 右边(python的运行时环境)
概念 说明 运行时状态(Current State of Python) 维护了解释器在执行字节码时不同的状态(比如正常状态和异常状态)之间切换的动作. 可以看作一个有穷状态机. 内存分配器(Memory Allocator) 负责python中创建对象时, 对内存的申请工作, 实际上它就是python运行时与C中malloc的一层接口. 对象/类型系统(Object/Type Structures) 包含了python中存在的各种内建对象, 比如整数, list和dict, 以及各种用户自定义的类型和对象.
0.1.3 中间(python的核心–解释器)
概念 说明 箭头 指示了python运行过程中的数据流方向 Scanner 对应词法分析, 将文件输入的python源代码 或 从命令行输入的一行行python代码切分为一个个的token Parser 对应语法分析, 在Scanner的分析结果上进行语法分析, 建立抽象语法树(AST) Compiler 是根据简历的AST生成指令集合 – python字节码(byte code), 就像java编译器和C#编译器所做的那样 Code Evaluator 执行字节码
0.1.4 右边和中间的关系
关系 说明 使用 解释器与右边的对象/类型系统, 内存分配器之间的箭头表示"使用"关系 修改 解释器与右边运行状态之间的箭头表示"修改"关系, 即python在执行的过程中会不断地修改当前解释器所处的状态, 在不同的状态之间切换
0.2 python源代码的组织
0.2.1 下载python2.5源码
0.2.2 解压源码后的目录结构
0.2.2.1 主要目录说明
概念 说明 Lib 该目录包含了Python自带的所有标准库, Lib中的库都是用Python语言编写的 Modules 该目录中包含了所有用C语言编写的模块, 比如random, cStringIO等. Modules中的模块是那些对速度要求非常严格的模块,而有一些对速度没有太严格要求的模块, 比如os, 就是用python编写,并且放在Lib目录下的 Parser 该目录中包含了python解释器中的Scanner和Parser部分, 即对python源代码进行词法分析和语法分析的部分. 除了这些, Parser目录下还包含了一些有用的工具, 这些工具能够根据python的语法自动生成python的词法和语法分析器, 与YACC非常类似 Objects 该目录中包含了所有python的内建对象, 包括整数, list, dict等. 同时, 该目录还包括了python在运行时需要的所有的内部使用对象的实现 Python 该目录下包含了python解释器中的Compiler和执行引擎部分,成python运行的核心所在
0.3 Unix/Linux环境下编译python
实验环境ubuntu 18.04 查看Python-2.5/Setup.py文件中的detect_tkinter函数, 来确定需要下载的tcl和tk版本
设置安装位置
export LOCAL= $HOME /.local
安装tcl
wget -c http://prdownloads.sourceforge.net/tcl/tcl8.4.20-src.tar.gz
tar xf tcl8.4.20-src.tar.gz
./unix/configure --prefix= $LOCAL
make -j4
make install
安装tk
wget -c http://prdownloads.sourceforge.net/tcl/tk8.4.20-src.tar.gz
tar xvf tk8.4.20-src.tar.gz
./unix/configure --prefix= $LOCAL
make -j4
make install
安装python
SVNVERSION= "Unversioned directory" ./configure --prefix= $LOCAL --enable-shared --with-tcltk-includes= "-I$LOCAL /include" --with-tcltk-libs= "-L$LOCAL /lib -ltcl8.6 -L$LOCAL /lib -ltk8.6"
export LD_LIBRARY_PATH= $LOCAL /lib:$LD_LIBRARY_PATH
make -j4
make install
目录
目录 说明 bin 可执行文件 lib 存放的是python的标准库; lib/python2.5/config下存放的是libpython2.5.a(用c语言对python扩展时需要用到这个静态库. include 头文件
选项
选项 说明 –prefix python将被安装的目标路径 –enable-shared 加此选项, 会编译成动态链接库. 不加此选项, bin目录下的python可执行文件是静态链接的 -I(大写的i) 预处理时查找头文件的范围 -L 用来告诉链接器到哪个路径下面去找动态链接库 -l(小写的L) 用来指定链接额外的库
参考
0.4 修改python源代码
0.4.1 输出python对象的接口
接口
[ Python- 2.5 / Include/ object. h]
PyAPI_FUNC ( int ) PyObject_Print ( PyObject * , FILE * , int ) ;
修改Python-2.5/Objects/intobject.c
文件
static int
int_print ( PyIntObject * v, FILE * fp, int flags)
{
PyObject* str = PyString_FromString ( "I am in int_print" ) ;
PyObject_Print ( str, stdout , 0 ) ;
printf ( "\n" ) ;
fprintf ( fp, "%ld" , v-> ob_ival) ;
return 0 ;
}
设置so动态库加载目录
export LD_LIBRARY_PATH= $LOCAL /lib:$LD_LIBRARY_PATH
编译python
make
将python的位置设置到环境变量中
export PATH= $LOCAL /bin:$PATH
在当前目录启动python, 就会链接到当前目录的so动态库
0.4.2 重定向标准输出
重定向到文件
标准输出 sys.stdout 也是C中stdout所代表的系统标准输出. 第一次执行sys.stdout时, 输出到屏幕 第二次执行sys.stdout时, 输出重定向到my_stdout.txt文件
重定向到idle
修改Python-2.5/Objects/intobject.c
文件
static PyObject *
int_repr ( PyIntObject * v)
{
if ( PyInt_AsLong ( v) == - 999 ) {
PyObject* str = PyString_FromString ( "I am in int_repr" ) ;
PyObject* out = PySys_GetObject ( "stdout" ) ;
if ( out != NULL ) {
PyObject_Print ( str, stdout , 0 ) ;
printf ( "\n" ) ;
}
}
char buf[ 64 ] ;
PyOS_snprintf ( buf, sizeof ( buf) , "%ld" , v-> ob_ival) ;
return PyString_FromString ( buf) ;
}
0.5 注意事项
Py_ssize_t
可以看作int
凡是以New结尾的API, 都以C++中的new操作符视之; 凡是以Malloc结尾的,都是以C中的malloc操作符视之。 在c语言中是怎么实现继承的?
第一章 python对象初探(20-06-01 *)
1.1 python内的对象
在 python中, 对象就是为C中的结构体在堆上申请的一块内存, 一般来说, 对象是不能被静态初始化的, 并且也不能在栈空间上生存。 唯一的例外就是类型对象, python中的所有的内建的类型对象(如整数类型对象, 字符串类型对象)都是被静态初始化的。 在python中,一个对象一旦被创建,它在内存中的大小就是不变的了。具有可变长度数据的对象只能在对象内维护一个指向一块可变大小的内存区域的指针。 对象的分类
对象 说明 fundamental 类型对象(type) numeric 数值对象(integer, float, boolean) sequence 容纳其它对象的序列集合对象(string, list, tuple, set) mapping 字典对象, 类似C++中的map的关联对象(dict) internal 内部对象, python虚拟机在运行时内部使用的对象(function, code, frame, module, method)
1.1.1 对象机制的基石——PyObject
在python中, 所有的东西都是对象, 而所有的对象都拥有一些相同的内容, 这些内容在PyObject中定义, PyObject是整个python对象机制的核心。 查看Python-2.5/Include/object.h
中的PyObject
typedef struct _object {
PyObject_HEAD
} PyObject;
查看Python-2.5/Include/object.h
中的PyObject_HEAD
#ifdef Py_TRACE_REFS
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif
#define PyObject_HEAD \
_PyObject_HEAD_EXTRA \
Py_ssize_t ob_refcnt; \
struct _typeobject * ob_type;
实际发布的python中, 是不会定义符号Py_TRACE_REFS的, PyObject的定义如下
typedef struct _object {
int ob_refcnt;
struct _typeobject * ob_type;
} PyObject;
在python中, 对象机制的核心: 一个是引用技术,一个是类型信息.
变量 说明 ob_refcnt 与python的内存管理机制有关, 它实现了基于引用计数的垃圾收集机制。 对于某一个对象A, 当有一个新的PyObject *
引用该对象时, A的引用计数应该增加;而当这个PyObject *
被删除时, A的引用计数应该减少。当A的引用计数减少到0时,A就可以从堆上被删除, 以释放出内存供别的对象使用 ob_type 包含对象的类型信息, 用来指定一个对象类型的类型对象
PyObject
中定义了每一个python对象都必须有的内容, 这些内容将出现在每一个python对象所占有的内存的最开始的字节中。剩下的内存用来保存对象自己特殊的信息。
[ intobject. h]
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
1.1.2 变长对象
表示变长对象的结构体
[ object. h]
#define PyObject_VAR_HEAD \
PyObject_HEAD
int ob_size;
typedef struct {
PyObject_VAR_HEAD
} PyVarObject;
从PyObject_VAR_HEAD
的定义可以看出,PyVarObject
只是对PyObject
的一个扩展而已。 因此对于任何一个PyVarObject
,其所占用的内存, 开始部分的字节的意义和PyObject
时一样的。 在python内部, 每一个对象都拥有相同的对象头部。这就使得在python中, 对对象的引用变得非常的统一, 我们只需要用一个PyObject *
指针就可以引用任意的一个对象。 而不论该对象实际是一个什么对象。
python中不同对象与PyObject、PyVarObject在内存布局上的关系
对象 说明 定长对象 不同对象的占用的内存大小是一样的 变长对象 不同对象占用的内存可能是不一样的
1.2 类型对象
占用内存空间的大小是对象的一种元信息, 这样的元信息是与对象所属类型密切相关的, 因此它一定会出现在与对象所对应的类型对象中. 现在我们可以来详细考察一下类型对象_typeobject:
[ object. h]
typedef struct _typeobject {
PyObject_VAR_HEAD
char * tp_name;
int tp_basicsize, tp_itemsize;
destructor tp_dealloc;
printfunc tp_print;
. . . . . .
hashfunc tp_hash;
ternaryfunc tp_call;
. . . . . .
} PyTypeObject;
类型名, tp_name
, 主要是python内部以及调试的时候使用. 创建该类型对象时分配内存空间大小的信息, 即tp_basicsize
和tp_itemsize
. 与该类型对象相关连的操作信息(就是诸如tp_print
这样的许多的函数指针). 类型的类型信息. PyTypeObject
对应python中的类
1.2.1 对象的创建
Python C API 创建
范型的API(或者称为AOL(Abstract Object Layer). 这类API都具有诸如PyObject_***的形式, 可以应用在任何Python对象身上
PyObject_Print ( int object) ;
PyObject_Print ( string object) ;
PyObject* intObj = PyObject_New ( PyObject, & PyInt_Type) ;
类型相关的API(或者称为COL(Concrete Object Layer). 这类API通常只能作用在某一种类型的对象上, 对于每一种内建对象, Python都提供了这样的一组API.
PyObject * intObj = PyInt_FromLong ( 10 ) ;
无论采用哪种C API, python内部最终都是直接分配内存, 因为python对于内建对象是无所不知的. 对于自定义的类A, python不可能事先提供PyA_New这样的API. 对于这种情况, python会通过A所对应的类型对象创建实例对象.
实际上, 在python完成运行环境的初始化之后, 符号"int"就对应着一个表示为<type ‘int’>的对象, 这个对象其实就是python内部的PyInt_Type.
''' 在new style class中, int是一个继承自object的类型, 类似于int对应着python内部的PyInt_Type, object在python内部则对应着PyBaseObject_Type
'''
int ( 10 )
使用C++中定义int
这种类型的方式, 以及在python内部, 这种继承关系是如何实现的。 说明 :标上序号的虚线箭头代表了创建整数对像的函数调用流程, 首先PyInt_Type
(对应python中的int类)中的tp_new
会被调用, 如果这个tp_new
为NULL
(真正的PyInt_Type
中并不为NULL
, 这里只是举例说明这种情况), 那么会到tp_base
指定的基类中去寻找tp_new
操作, PyBaseObject_Type
(对应python中的object基类)的tp_new
指向了object_new
. 新式类中,所有的类都是以object
为基类的, 所以最终会找到一个不为NULL
的tp_new
. 在object_new
中, 会访问PyInt_Type
中记录的tp_basicsize
信息, 继而完成申请内存的操作。这个信息记录着一个整数对象应该占用多大内存, 在python源码中, 这个值被设置成了sizeof(PyIntObject)
. 在调用tp_new
完成“创建对象”之后, 流程会转向PyInt_Type
的tp_init
, 完成“初始化对象”的工作。对应到C++中, tp_new
可以视为new
操作符, 而tp_init
则可视为类的构造函数。(这里只说明了类型对象在实例对象创建过程中的作用,实际上, 会有些不同 )
1.2.2 对象的行为
在Python-2.5/Include/object.h
文件中的函数PyTypeObject
中定义了大量的函数指针, 这些函数指针最终都会指向某个函数, 或者指向NULL. 这些函数指针可以视为类型对象中所定义的操作, 而这些操作直接决定着一个对象在运行时所表现出的行为.
比如PyTypeObject
中的tp_hash
指明对于该类型的对象, 如何生成其hash值。我们看到tp_hash
是一个hashfunc
类型的变量, 在object.h
中, hashfunc
实际上是一个函数指针: typedef long (*hashfunc)(PyObject *)
在这些操作信息中, 有三组非常重要的操作族, 在PyTypeObject
中, 它们是tp_as_number
、tp_as_sequence
、tp_as_mapping
. 他们分别指向PyNumberMethods
、PySequenceMethods
和PyMappingMethods
函数族。PyNumberMethods
函数族示例:
[ object. h]
typedef PyObject * ( * binaryfunc) ( PyObject * , PyObject * ) ;
typedef struct {
binaryfunc nb_add;
binaryfunc nb_subtract;
. . .
} PyNumberMethods;
对于一种类型来说, 它完全可以同时定义三个函数族中的所有操作。 下面示例中, 既有数值对象的特性, 也有关联对象的特性:
1.2.3 类型的类型
通过PyType_Type
来确定一个对象为类型对象
[ typeobject. c]
"type" ,
sizeof ( PyHeapTypeObject) ,
sizeof ( PyMemberDef) ,
. . .
} ;
PyType_Type
与一般PyTypeObject
的关系 PyInt_Type
和PyType_Type
是怎么建立关系的。在python中, 每一个对象都将自己的引用计数、类型信息保存在开始的部分中。为了方便对这部分内存的初始化, python中提供了几个有用的宏:
[ object. h]
#ifdef Py_TRACE_REFS
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif
#define PyObject_HEAD \
_PyObject_HEAD_EXTRA \
Py_ssize_t ob_refcnt; \
struct _typeobject *ob_type;
#define PyObject_HEAD_INIT(type) \
_PyObject_EXTRA_INIT \
1, type,
[ object. h]
#define PyObject_VAR_HEAD \
PyObject_HEAD \
Py_ssize_t ob_size;
#define Py_INVALID_SIZE (Py_ssize_t)-1
typedef struct _object {
PyObject_HEAD
} PyObject;
typedef struct {
PyObject_VAR_HEAD
} PyVarObject;
以PyInt_Type
为例, 可以更清晰地看到一般的类型对象和这个特立独行的PyType_Type
对象之间的关系
[ intobject. c]
PyTypeObject PyInt_Type = {
PyObject_HEAD_INIT ( & PyType_Type)
0 ,
"int" ,
sizeof ( PyIntObject) ,
. . .
} ;
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
变量 说明 ob_type 指向对象的类型 tp_base 指向对象的父类
1.3 Python对象的多态性
通过PyObject
和PyTypeObject
, python利用C语言完成了C++所提供的对象的多态的特性. 在python创建一个对象, 比如PyIntObject
对象时, 会分配内存, 进行初始化. 然后python内部会用一个PyObject *
变量来保存和维护这个对象. 其它对象也与此类似, 所以在python内部各个函数之间传递的都是一种泛型指针-PyObject *
. 这个指针所指的对象究竟是什么类型的, 只能从指针所指对象的ob_type域动态进行判断, 而正是通过这个域, python实现了多态机制.
Print函数示例 如果传给Print
的指针是一个PyIntObject*
, 那么它就会调用PyIntObject
对象对应的类型对象中定义的输出操作, 如果指针是一个PyStringObject*
, 那么就会调用PyStringObject
对象对应的类型对象中定义的输出操作.
void Print ( PyObject* object)
{
object-> ob_type-> tp_print ( object) ;
}
[ object. c]
long PyObject_Hash ( PyObject * v)
{
PyTypeObject * tp = v-> ob_type;
if ( tp-> tp- hash != NULL )
return ( * tp-> tp_hash) ( v) ;
. . .
}
1.4 引用计数
c和c++, 可以由程序员申请和释放内存. 容易造成内存泄露 python由语言本身负责内存的管理和维护, 即采用垃圾收集机制. 提高开发效率, 降低bug发生几率. 损失了一部分运行效率. python通过对一个对象的引用技术的管理来维护对象在内存中的存在与否. python中的每一个东西都是对象, 都有一个ob_refcnt
变量. 这个变量维护着该对象的引用计数, 从而也最终决定着该对象的创建与消亡. 在python中, 主要通过Py_INCREF(op)
和Py_DECREF(op)
两个宏来增加和减少一个对象的引用计数. 当一个对象的引用技术减少到0之后, Py_DECREF
将调用该对象的析构函数(借用c++的术语, 实际是对象对应的类型对象中定义的函数指针tp_dealloc
来指定的)来释放该对象所占有的内存和系统资源. 但是调用析构函数并不意味着最终一定会调用free
释放内存空间, 如果真是这样的话, 那频繁地申请, 释放内存空间会使python的执行效率大打折扣. 一般来说, python中大量采用了内存对象池的技术, 使用这种技术可以避免频繁地申请和释放内存空间. 因此在析构时, 通常都是将对象占用的空间归还到内存池中. 从python的对象体系来看, 各个对象提供了不同的对象销毁事件处理函数, 而事件的注册动作正是在各个对象对应的类型对象中静态完成的. PyObject
中的ob_refcnt
是一个32位的整形变量.类型对象, 永远不会被析构. 每一个对象中指向类型对象的指针不被视为对类型对象的引用. 在每一个对象创建的时候, python提供了一个_Py_NewReference(op)
宏来将对象的引用计数初始化为1. python在最终发行时, 引用计数的宏所对应的实际代码.
[ object. h]
#define _Py_NewReference(op) ((op)->ob_refcnt = 1)
#define _Py_dealloc(op) ((*(op)->ob_type->tp_dealloc)((PyObject *)(op)))
#define Py_INCREF(op) ((op)->ob_refcnt++)
#define Py_DECREF(op)
if ( -- ( op) -> ob_refcnt != 0 )
;
else
_Py_Dealloc ( ( PyObject * ) ( op) )
#define Py_XINCREF(op) if ((op) == NULL); else Py_INCREF(op)
#define Py_XDECREF(op) if ((op) == NULL); else PY_DECREF(op)
第2章 python中的整数对象
2.1 初识PyIntObject
对象
python中的 “整数” 这个概念的实现是通过PyIntObject
对象来完成的. 对象分类 根据对象维护数据的可变性将对象分为: 可变对象、不可变对象 根据同一类型不同对象占用的内存大小是否相同可将对象分为: 定长对象、变长对象 PyIntObject
的元信息实际上保存在对应的类型对象PyInt_Type
中
[ intobject. c]
PyTypeObject PyInt_Type = {
PyObject_HEAD_INIT ( & PyType_Type)
0 ,
"int" ,
sizeof ( PyIntObject) ,
0 ,
( destructor) int_dealloc,
( printfunc) int_print,
0 ,
0 ,
( cmpfunc) int_compare,
( reprfunc) int_repr,
& int_as_number,
0 ,
0 ,
( hashfunc) int_hash,
0 ,
( reprfunc) int_repr,
PyObject_GenericGetAttr,
0 ,
0 ,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
Py_TPFLAGS_BASETYPE,
int_doc,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
int_methods,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
int_new,
( freefunc) int_free,
} ;
方法 说明 int_dealloc PyIntObject对象的析构操作 int_free PyIntObject对象的释放操作 int_repr 转化成PyStringObject对象 int_hash 获得HASH值 int_print 打印PyIntObject对象 int_compare 比较操作 int_as_number 数值操作集合 int_methods 成员函数集合
2.2 PyIntObject
对象的创建和维护
2.2.1 对象创建的3中途径
在python自身的实现中, 几乎都是调用C API来创建内建实例对象的。 示例
[ intobject. h]
PyObject * PyInt_FromLong ( long ival)
PyObject * PyInt_FromString ( char * s, char * * pend, int base)
#ifdef Py_USING_UNICODE
PyObject * PyInt_FromUnicode ( Py_UNICODE * s, int length, int base)
#endif
2.2.2 小整数对象
为了性能考虑,python中对小整数有专门的缓存池,这样就不需要每次使用小整数对象时去用malloc分配内存以及free释放内存。 小整数的范围默认设定为[-5, 257)
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
static PyIntObject * small_ints[ NSMALLNEGINTS + NSMALLPOSINTS] ;
#endif
2.2.3 大整数对象
python运行环境将提供一块内存空间, 这些内存空间由这些大整数轮流使用。
[ intobject. c]
#define BLOCK_SIZE 1000
#define BHEAD_SIZE 8
#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
struct _intblock {
struct _intblock * next;
PyIntObject objects[ N_INTOBJECTS] ;
} ;
typedef struct _intblock PyIntBlock;
static PyIntBlock * block_list = NULL ;
static PyIntObject * free_list = NULL ;
2.2.4 添加和删除
[ intobject. c]
PyObject* PyInt_FromLong ( long ival)
{
register PyIntObject * v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
if ( - NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ ival + NSMALLNEGINTS] ;
Py_INCREF ( v) ;
#ifdef COUNT_ALLOCS
if ( ival >= 0 )
quick_int_allocs++ ;
else
quick_neg_int_allocs++ ;
#endif
return ( PyObject * ) v;
}
#endif
if ( free_list == NULL ) {
if ( ( free_list = fill_free_list ( ) ) == NULL )
return NULL ;
}
v = free_list;
free_list = ( PyIntObject * ) v-> ob_type;
PyObject_INIT ( v, & PyInt_Type) ;
v-> ob_ival = ival;
return ( PyObject * ) v;
}
2.2.4.1 使用小整数对象池
如果NSMALLNEGINTS + NSMALLPOSINTS > 0
成立, 那么python认为小整数对象池机制被激活了, 此时会创建对象池(PyIntObject
指针数组). PyInt_FromLong
会首先检查传入的long值是否属于小整数的范围, 如果确实属于小整数, 一切就变得简单了, 只需要返回在小整数对象池中的对应的对象就可以了。 如果小整数对象池机制没有被激活, 或者传入的long值不是属于小整数, python就会转向由block_list
维护的通用整数对象池。 正如前面我们所描述的, python需要在通过free_list在某块block的objects中, 寻找一块可用于存储新的PyIntObject
对象的内存。
2.2.4.2 创建通用整数对象池
显然,当首次调用PyInt_FromLong
时,free_list必定为NULL, 这时python会调用fill_free_list
, 创建新的block, 从而也就创建了新的空闲内存。需要注意的是, python对fill_free_list
的调用不光会发生在对PyInt_FromLong
的首次调用时,在python运行期间, 只要所有的block的空闲内存都被使用完了, 就会导致free_list
变为NULL, 从而在下一次PyInt_FromLong
的调用时激发对fill_free_list
的调用。
[ intobject. c]
static PyIntObject *
fill_free_list( void)
{
PyIntObject * p, * q;
/ * Python's object allocator isn' t appropriate for large blocks. * /
// 申请大小为sizeof( PyIntBlock) 的内存空间, 并链接到已有的block list 中
p = ( PyIntObject * ) PyMem_MALLOC( sizeof( PyIntBlock) ) ;
if ( p == NULL)
return ( PyIntObject * ) PyErr_NoMemory( ) ;
( ( PyIntBlock * ) p) - > next = block_list;
block_list = ( PyIntBlock * ) p;
/ * Link the int objects together, from rear to front, then return
the address of the last int object in the block. * /
// 将PyIntBlock中的PyIntObject数组——objects- 转变成单向链表
p = & ( ( PyIntBlock * ) p) - > objects[ 0 ] ;
q = p + N_INTOBJECTS;
while ( - - q > p)
q- > ob_type = ( struct _typeobject * ) ( q- 1 ) ;
q- > ob_type = NULL;
return p + N_INTOBJECTS - 1 ;
}
在fill_free_list
中, 会首先申请一个新的PyIntBlock
结构,如图所示 这时block中的objects
还仅仅是一个PyIntObject
对象的数组, 接下来, python将objects
中的所有PyIntObject
对象通过指针依次连接起来, 从而将数组转变成一个单向链表。 python从objects数组的最后一个元素开始链接的过程, 在整个链接过程中, python使用了PyObject
中的ob_type
指针作为 连接指针。
2.2.4.3 使用通用整数对象池
FQA
PyIntObject
是怎么知道它的元信息为PyInt_Type
的? 对象是由类创建的, 在创建对象的过程, 就会赋给类型对象相应的元信息 PyObject中的双向链表是怎么用来进行垃圾回收的? PyType_Type
和PyTypeObject
? 所有用户自定义class
所对应的PyTypeObject
对象都是通过PyType_Type
这个对象创建的 静态的整数对象的定义
[ intobject. h]
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;