python源码剖析笔记

第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 解压源码后的目录结构

python目录结构

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版本

  1. 设置安装位置
export LOCAL=$HOME/.local
  1. 安装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
  1. 安装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
  1. 安装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头文件

选项

选项说明
–prefixpython将被安装的目标路径
–enable-shared加此选项, 会编译成动态链接库. 不加此选项, bin目录下的python可执行文件是静态链接的
-I(大写的i)预处理时查找头文件的范围
-L用来告诉链接器到哪个路径下面去找动态链接库
-l(小写的L)用来指定链接额外的库
  1. 参考

0.4 修改python源代码

0.4.1 输出python对象的接口

  1. 接口
[Python-2.5/Include/object.h]
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int); 
  1. 修改Python-2.5/Objects/intobject.c文件
/* ARGSUSED */
static int
int_print(PyIntObject *v, FILE *fp, int flags)
     /* flags -- not used but required by interface */
{
    PyObject* str = PyString_FromString("I am in int_print");	// 用于从C中的原生字符数组中创建出python中的字符串对象
    /* 第二个参数指明的是输出目标. stdout指定了输出目标为标准输出. 
    命令行激活的python, 使用的是stdout, idle的输出目标不是stdout, 就不会显示信息
    */
    PyObject_Print(str, stdout, 0);		
    printf("\n");
                                                                                                                                              
    fprintf(fp, "%ld", v->ob_ival);
    return 0;
}
  1. 设置so动态库加载目录
export LD_LIBRARY_PATH=$LOCAL/lib:$LD_LIBRARY_PATH
  1. 编译python
# 第一次执行configure的时候, 需要添加--enable-shared, 才能编译成动态链接库
# SVNVERSION="Unversioned directory" ./configure --prefix=<python将被安装的目标路径> --enable-shared
make
  1. 将python的位置设置到环境变量中
# 设置以后, 可以直接使用python在命令中启动python交互界面
export PATH=$LOCAL/bin:$PATH
  1. 在当前目录启动python, 就会链接到当前目录的so动态库
    print_100

0.4.2 重定向标准输出

  1. 重定向到文件
    在这里插入图片描述
  • 标准输出 sys.stdout 也是C中stdout所代表的系统标准输出.
  • 第一次执行sys.stdout时, 输出到屏幕
  • 第二次执行sys.stdout时, 输出重定向到my_stdout.txt文件
    在这里插入图片描述
  1. 重定向到idle
  • 修改Python-2.5/Objects/intobject.c文件
static PyObject *
int_repr(PyIntObject *v)
{   
	/* PyInt_AsLong 将python的整数对象转换为C中的int值.
	设置条件, 用来筛选出我们需要的信息*/                   
    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);
}               
  • python交互界面
    在这里插入图片描述
  • idle
    在这里插入图片描述
    在这里插入图片描述

0.5 注意事项

第一章 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)

https://flaggo.github.io/python3-source-code-analysis/objects/object/object-category.jpg

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 pointers to support a doubly-linked list of all live heap objects. */
#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
    
/* PyObject_HEAD defines the initial segment of every PyObject. */                                                                            
#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	// PyObject
	long ob_ival;	// 保存整数值
} PyIntObject;

1.1.2 变长对象

  1. 表示变长对象的结构体
[object.h]
#define PyObject_VAR_HEAD		\
	PyObject_HEAD
	int ob_size;	/* Number of items in variable part 元素的个数*/

typedef struct {
	PyObject_VAR_HEAD
} PyVarObject;
  • PyObject_VAR_HEAD的定义可以看出,PyVarObject只是对PyObject的一个扩展而已。
  • 因此对于任何一个PyVarObject,其所占用的内存, 开始部分的字节的意义和PyObject时一样的。
  • 在python内部, 每一个对象都拥有相同的对象头部。这就使得在python中, 对对象的引用变得非常的统一, 我们只需要用一个PyObject *指针就可以引用任意的一个对象。 而不论该对象实际是一个什么对象。
  1. python中不同对象与PyObject、PyVarObject在内存布局上的关系
    在这里插入图片描述
对象说明
定长对象不同对象的占用的内存大小是一样的
变长对象不同对象占用的内存可能是不一样的

1.2 类型对象

  • 占用内存空间的大小是对象的一种元信息, 这样的元信息是与对象所属类型密切相关的, 因此它一定会出现在与对象所对应的类型对象中. 现在我们可以来详细考察一下类型对象_typeobject:
[object.h]
typedef struct _typeobject {
	PyObject_VAR_HEAD
	char *tp_name;	/* For printing, in format "<module>.<name>" */
	int tp_basicsize, tp_itemsize;	/* For allocation */
	
	/* Methods to implement standard operations */
	destructor tp_dealloc;
	printfunc tp_print;
	......
	/* More standard operations (here for binary compatibility) */
	hashfunc tp_hash;
	ternaryfunc tp_call;
	......
	} PyTypeObject;
  • 类型名, tp_name, 主要是python内部以及调试的时候使用.
  • 创建该类型对象时分配内存空间大小的信息, 即tp_basicsizetp_itemsize.
  • 与该类型对象相关连的操作信息(就是诸如tp_print这样的许多的函数指针).
  • 类型的类型信息.
  • PyTypeObject 对应python中的

1.2.1 对象的创建

  1. Python C API 创建
  • 范型的API(或者称为AOL(Abstract Object Layer). 这类API都具有诸如PyObject_***的形式, 可以应用在任何Python对象身上
// API内部会有一整套机制确定最终调用的函数是哪一个
PyObject_Print(int object);
PyObject_Print(string object);
PyObject* intObj = PyObject_New(PyObject, &PyInt_Type); // 创造一个整数对象
  • 类型相关的API(或者称为COL(Concrete Object Layer). 这类API通常只能作用在某一种类型的对象上, 对于每一种内建对象, Python都提供了这样的一组API.
// 创建一个值为10的整数对象
PyObject *intObj = PyInt_FromLong(10);
  1. 无论采用哪种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)	# 实际上是通过PyInt_Type创建了一个整数对象
  • 使用C++中定义int这种类型的方式, 以及在python内部, 这种继承关系是如何实现的。
    在这里插入图片描述说明:标上序号的虚线箭头代表了创建整数对像的函数调用流程, 首先PyInt_Type(对应python中的int类)中的tp_new会被调用, 如果这个tp_newNULL(真正的PyInt_Type中并不为NULL, 这里只是举例说明这种情况), 那么会到tp_base指定的基类中去寻找tp_new操作, PyBaseObject_Type(对应python中的object基类)的tp_new指向了object_new. 新式类中,所有的类都是以object为基类的, 所以最终会找到一个不为NULLtp_new. 在object_new中, 会访问PyInt_Type中记录的tp_basicsize信息, 继而完成申请内存的操作。这个信息记录着一个整数对象应该占用多大内存, 在python源码中, 这个值被设置成了sizeof(PyIntObject). 在调用tp_new完成“创建对象”之后, 流程会转向PyInt_Typetp_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_numbertp_as_sequencetp_as_mapping. 他们分别指向PyNumberMethodsPySequenceMethodsPyMappingMethods函数族。PyNumberMethods函数族示例:
[object.h]
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);

typedef struct {
	binaryfunc nb_add;	// 指定了数值对象进行加法操作时的具体行为
	binaryfunc nb_subtract;
	...
} PyNumberMethods;
  • 对于一种类型来说, 它完全可以同时定义三个函数族中的所有操作。 下面示例中, 既有数值对象的特性, 也有关联对象的特性:
    在这里插入图片描述

1.2.3 类型的类型

  • 通过PyType_Type来确定一个对象为类型对象
[typeobject.c]
/* 所有用户自定义class所对应的PyTypeObject对象都是通过PyType_Type这个对象创建的
PyTypeObject PyType_Type = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,									/* ob_size */
	"type",								/* tp_name */
	sizeof(PyHeapTypeObject),		/* tp_basicsize */
	sizeof(PyMemberDef),				/* tp_itemsize */
	...
};
  • PyType_Type 与一般PyTypeObject的关系
    在这里插入图片描述
  • PyInt_TypePyType_Type是怎么建立关系的。在python中, 每一个对象都将自己的引用计数、类型信息保存在开始的部分中。为了方便对这部分内存的初始化, python中提供了几个有用的宏:
[object.h]
#ifdef Py_TRACE_REFS 
/* Define pointers to support a doubly-linked list of all live heap objects. */
#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               
                     
/* PyObject_HEAD defines the initial segment of every PyObject. */
#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,      
  • PyObjectPyVarObject的定义
[object.h]
/* PyObject_VAR_HEAD defines the initial segment of all variable-size
 * container objects.  These end with a declaration of an array with 1
 * element, but enough space is malloc'ed so that the array actually
 * has room for ob_size elements.  Note that ob_size is an element count,
 * not necessarily a byte count.
 */                  
#define PyObject_VAR_HEAD       \
    PyObject_HEAD           \
    Py_ssize_t ob_size; /* Number of items in variable part */
#define Py_INVALID_SIZE (Py_ssize_t)-1
                     
/* Nothing is actually declared to be a PyObject, but every pointer to
 * a Python object can be cast to a PyObject*.  This is inheritance built
 * by hand.  Similarly every pointer to a variable-size Python object can,
 * in addition, be cast to PyVarObject*.
 */                  
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) // *_ob_next 0, *_ob_prev 0, ob_refcent 1, ob_type &PyType_Type
	0,	//  ob_size 0
	"int",
	sizeof(PyIntObject),
	...
};
  • PyIntObject
typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;
  • 整数对象运行时的图像表现
变量说明
ob_type指向对象的类型
tp_base指向对象的父类

在这里插入图片描述

1.3 Python对象的多态性

通过PyObjectPyTypeObject, 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);
}
  • Hash函数示例
[object.c]
long PyObject_Hash(PyObject *v)
{
	PyTypeObject *tp = v->ob_type;
	if (tp->tp-hash != NULL)
		return (*tp->tp_hash)(v);	// tp前面的*, 有和没有都是一样的. -> 的优先级大于*
		...
}

1.4 引用计数

  1. c和c++, 可以由程序员申请和释放内存. 容易造成内存泄露
  2. python由语言本身负责内存的管理和维护, 即采用垃圾收集机制. 提高开发效率, 降低bug发生几率. 损失了一部分运行效率. python通过对一个对象的引用技术的管理来维护对象在内存中的存在与否. python中的每一个东西都是对象, 都有一个ob_refcnt变量. 这个变量维护着该对象的引用计数, 从而也最终决定着该对象的创建与消亡.
  3. 在python中, 主要通过Py_INCREF(op)Py_DECREF(op)两个宏来增加和减少一个对象的引用计数. 当一个对象的引用技术减少到0之后, Py_DECREF将调用该对象的析构函数(借用c++的术语, 实际是对象对应的类型对象中定义的函数指针tp_dealloc来指定的)来释放该对象所占有的内存和系统资源. 但是调用析构函数并不意味着最终一定会调用free释放内存空间, 如果真是这样的话, 那频繁地申请, 释放内存空间会使python的执行效率大打折扣. 一般来说, python中大量采用了内存对象池的技术, 使用这种技术可以避免频繁地申请和释放内存空间. 因此在析构时, 通常都是将对象占用的空间归还到内存池中.
  4. 从python的对象体系来看, 各个对象提供了不同的对象销毁事件处理函数, 而事件的注册动作正是在各个对象对应的类型对象中静态完成的.
  5. PyObject中的ob_refcnt是一个32位的整形变量.
  6. 类型对象, 永远不会被析构. 每一个对象中指向类型对象的指针不被视为对类型对象的引用.
  7. 在每一个对象创建的时候, python提供了一个_Py_NewReference(op)宏来将对象的引用计数初始化为1.
  8. 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))
/* Macros to use in case the object pointer may be NULL; */
#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,									// 因为PyTypeObject是一个PyVarObject对象, 因此这里需要设置大小为0
    "int",								// 用来打印的字段,比如我们type(3)返回的int字符串就是来自这里。
    sizeof(PyIntObject),				// 对象基本大小
    0,									// 如果是可变大小对象,这个字段是对象里面存储项的大小
    (destructor)int_dealloc,        /* tp_dealloc */
    (printfunc)int_print,           /* tp_print */
    0,                  /* tp_getattr */
    0,                  /* tp_setattr */
    (cmpfunc)int_compare,           /* tp_compare */
    (reprfunc)int_repr,         /* tp_repr */
    &int_as_number,             /* tp_as_number 确定了对于一个整数对象, 这些数值操作应该如何进行*/ 
    0,                  /* tp_as_sequence */
    0,                  /* tp_as_mapping */
    (hashfunc)int_hash,         /* tp_hash */
        0,                  /* tp_call */
        (reprfunc)int_repr,         /* tp_str */
    PyObject_GenericGetAttr,        /* tp_getattro */
    0,                  /* tp_setattro */
    0,                  /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
        Py_TPFLAGS_BASETYPE,        /* tp_flags */
    int_doc,                /* tp_doc */
    0,                  /* tp_traverse */
    0,                  /* tp_clear */
    0,                  /* tp_richcompare */
    0,                  /* tp_weaklistoffset */
    0,                  /* tp_iter */
    0,                  /* tp_iternext */
    int_methods,                /* tp_methods */
    0,                  /* tp_members */
    0,                  /* tp_getset */
    0,                  /* tp_base */
    0,                  /* tp_dict */
    0,                  /* tp_descr_get */
    0,                  /* tp_descr_set */
    0,                  /* tp_dictoffset */
    0,                  /* tp_init */
    0,                  /* tp_alloc */
    int_new,                /* tp_new */
    (freefunc)int_free,                 /* tp_free */
};

方法说明
int_deallocPyIntObject对象的析构操作
int_freePyIntObject对象的释放操作
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]
/* 
* 其中PyInt_FromUnicode最终调用PyInt_FromString, 而PyInt_String最终调用PyInt_FromLong
*/
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	// Number_Small_Positive_Integers
#define NSMALLPOSINTS       257
#endif
#ifndef NSMALLNEGINTS	// Number_Small_Negtive_Integers
#define NSMALLNEGINTS       5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they
   can be shared.
   The integers that are saved are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];	// 小整数对象的对象池, 为PyIntObject对象指针数组。[]符号的优先级大于*
#endif

2.2.3 大整数对象

  • python运行环境将提供一块内存空间, 这些内存空间由这些大整数轮流使用。
[intobject.c]
#define BLOCK_SIZE  1000    /* 1K less typical malloc overhead */
#define BHEAD_SIZE  8   /* Enough for a 64-bit pointer */
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))

struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INTOBJECTS];
};

typedef struct _intblock PyIntBlock;	// 这个结构里维护了一块内存(block), 其中保存了一些PyIntObject对象

static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;

2.2.4 添加和删除

  • 详解PyInt_FromLong
[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;
    }
    /* Inline PyObject_New */
    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结构,如图所示
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200603145058670.png
这时block中的objects还仅仅是一个PyIntObject对象的数组, 接下来, python将objects中的所有PyIntObject对象通过指针依次连接起来, 从而将数组转变成一个单向链表。 python从objects数组的最后一个元素开始链接的过程, 在整个链接过程中, python使用了PyObject中的ob_type指针作为 连接指针。

2.2.4.3 使用通用整数对象池

FQA

  • PyIntObject是怎么知道它的元信息为PyInt_Type的?
    对象是由类创建的, 在创建对象的过程, 就会赋给类型对象相应的元信息
  • PyObject中的双向链表是怎么用来进行垃圾回收的?
  • PyType_TypePyTypeObject?
    所有用户自定义class所对应的PyTypeObject对象都是通过PyType_Type这个对象创建的
  • 静态的整数对象的定义
[intobject.h]
// python中的整数对象PyIntObject实际上就是对C中原生类型long的一个简单包装
typedef struct {
	PyObject_HEAD
	long ob_ival;
} PyIntObject;
  • c语言中是怎么判断加法是否溢出的?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值