大师兄的Python源码学习笔记(一): 准备工作
大师兄的Python源码学习笔记(三): 整数对象
一、关于对象
1. 关于对象
- 对象是Python中的核心概念,在Python中一切皆对象。
- 大师兄的Python学习笔记(二): 面向对象和类
2. 对象分类
- Fundamental 对象: 类型对象
- Numeric 对象: 数值对象
- Sequence 对象: 容纳其他对象的序列集合对象
- Mapping 对象: 类似 C++中的 map 的关联对象
- Internal 对象: Python 虚拟机在运行时内部使用的对象
二、C中的Python对象
- Python中的对象是C中的结构体在堆上申请的一块内存。
- 对象一旦创建,在内存中的大小是不变的。
1. 关于PyObject
- PyObject是Python对象机制的核心,用于定义对象。
- 源码在include/object.h。
1.1 PyObject结构
PyObject |
---|
ob_refcnt |
*ob_type |
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
#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;
- PyObject 包含 一个用于垃圾回收的双向链表:_PyObject_HEAD_EXTRA。
- 一个引用计数变量:ob_refcnt 。
- ob_refcnt与Python的内存管理机制有关,它实现了基于计数的垃圾收集机制。
- 当有新的PyObject* 引用该对象时,引用计数增加,删除时减少。
- 当引用计数为0时释放内存。
- 一个类型对象指针:ob_type。
- ob_type结构体用来指定一个对象类型的类型对象。
1.2 定长对象与变长对象
- 像整数对象这样不包含可变长度数据的对象称为定长对象,占用的内存大小一样。
- 像字符串或容器这样包含可变长度数据的对象称为变长对象, 占用的内存大小可能不一样。
- 变长对象的结构体用PyVarObject表示。
PyVarObject |
---|
ob_refcnt |
*ob_type |
ob_size |
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
- 从代码可以看出PyVarObject实际是PyObject的扩展,ob_size指明了对象中一共容纳元素的个数。
- 在Python中,每一个对象都拥有相同的头部,PyObject*指针可以引用任意一个对象,不论该对象实际是什么对象。
1.3 类型对象
- 类型对象_typeobject用于储存在内存分配空间创建对象时,对象的元信息,包括:
1) 类型名, tp_name, 主要用于 Python 内部调试用。
2) 创建该类型对象时分配的空间大小信息,即 tp_basicsize 和 tp_itemsize。
3) 与该类型对象相关的操作信息(如 tp_print 这样的函数指针)。
4) 一些对象属性。
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
or tp_reserved (Python 3) */
reprfunc tp_repr;
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/* More standard operations (here for binary compatibility) */
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
/* Functions to access object as input/output buffer */
PyBufferProcs *tp_as_buffer;
/* Flags to define presence of optional/expanded features */
unsigned long tp_flags;
const char *tp_doc; /* Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
traverseproc tp_traverse;
/* delete references to contained objects */
inquiry tp_clear;
/* Assigned meaning in release 2.1 */
/* rich comparisons */
richcmpfunc tp_richcompare;
/* weak reference enabler */
Py_ssize_t tp_weaklistoffset;
/* Iterators */
getiterfunc tp_iter;
iternextfunc tp_iternext;
/* Attribute descriptor and subclassing stuff */
struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
descrsetfunc tp_descr_set;
Py_ssize_t tp_dictoffset;
initproc tp_init;
allocfunc tp_alloc;
newfunc tp_new;
freefunc tp_free; /* Low-level free-memory routine */
inquiry tp_is_gc; /* For PyObject_IS_GC */
PyObject *tp_bases;
PyObject *tp_mro; /* method resolution order */
PyObject *tp_cache;
PyObject *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
destructor tp_finalize;
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
Py_ssize_t tp_frees;
Py_ssize_t tp_maxalloc;
struct _typeobject *tp_prev;
struct _typeobject *tp_next;
#endif
} PyTypeObject;
1.3.1 创建对象
- Python内部有两种创建对象的方式:AOL(Abstract Object Layer,范型API)和COL(Concrete Object Layer,与类型相关的API)。
1) 范型API
- 范型API 通常形如PyObject_***的形式。
- 范型API可以应用在任何 Python 对象上, 如PyObject_Print, 可以PyObject_Print(string object)也可以PyObject_Print(int object), API内部会有一套机制确定最终调用哪个函数。
PyObject* intobj = PyObject_New(Pyobject, &PyInt_Type);
2) 与类型相关的API
- COL API通常只能作用在某种类型的对象上,对于每一种内建对象 Python 都提供了这样一组 API。例如整数对象,我们可以利用如下的 API 创建:
PyObject *intObj = PyInt_FromLong(100)
- 无论哪种C API, 内存最终都是直接分配内存,因为Python对于内建对象是无所不知的。
- 但是对用户自定义的类型,由于Python无法事先了解API,所以需要先通过对应的类型对象创建实例对象。
1.3.2 对象的行为
- 在 PyTypeObject 中定义了大量的函数指针,这些函数指针可视为类型对象中所定义的操作,决定着一个对象在运行时所表现出的行为。
- 在操作信息中,有三组非常重要的操作族,分别是:
1) PyNumberMethods *tp_as_number
- PyNumberMethods 定义了一个数值对象该支持的操作。
- 一个数值对象如 整数对象,那么它的类型对象 PyLong_Type中tp_as_number.nb_add 就指定了它进行加法操作时的具体行为。
typedef struct { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions in the slot functions themselves. */ binaryfunc nb_add; binaryfunc nb_subtract; binaryfunc nb_multiply; binaryfunc nb_remainder; binaryfunc nb_divmod; ternaryfunc nb_power; unaryfunc nb_negative; unaryfunc nb_positive; unaryfunc nb_absolute; inquiry nb_bool; unaryfunc nb_invert; binaryfunc nb_lshift; binaryfunc nb_rshift; binaryfunc nb_and; binaryfunc nb_xor; binaryfunc nb_or; unaryfunc nb_int; void *nb_reserved; /* the slot formerly known as nb_long */ unaryfunc nb_float; binaryfunc nb_inplace_add; binaryfunc nb_inplace_subtract; binaryfunc nb_inplace_multiply; binaryfunc nb_inplace_remainder; ternaryfunc nb_inplace_power; binaryfunc nb_inplace_lshift; binaryfunc nb_inplace_rshift; binaryfunc nb_inplace_and; binaryfunc nb_inplace_xor; binaryfunc nb_inplace_or; binaryfunc nb_floor_divide; binaryfunc nb_true_divide; binaryfunc nb_inplace_floor_divide; binaryfunc nb_inplace_true_divide; unaryfunc nb_index; binaryfunc nb_matrix_multiply; binaryfunc nb_inplace_matrix_multiply; } PyNumberMethods;
2) PySequenceMethods *tp_as_sequence
- 同上PySequenceMethods 定义了一个序列对象的操作。
typedef struct { lenfunc sq_length; binaryfunc sq_concat; ssizeargfunc sq_repeat; ssizeargfunc sq_item; void *was_sq_slice; ssizeobjargproc sq_ass_item; void *was_sq_ass_slice; objobjproc sq_contains; binaryfunc sq_inplace_concat; ssizeargfunc sq_inplace_repeat; } PySequenceMethods;
3) PyMappingMethods *tp_as_mapping
- 同上PyMappingMethods 定义了一个关联对象的操作。
typedef struct { lenfunc mp_length; binaryfunc mp_subscript; objobjargproc mp_ass_subscript; } PyMappingMethods;
- 同一种类型可以同时定义三个函数族中的所有操作,比如一个对象同时有数值和关联对象的特征,比如:
class my_int(int):
def __getitem__(self,key):
return key+str(self)
a = my_int(1)
b = my_int(1)
print(a['key'])
>key1
print(b['key'])
>key1
- 在以上代码中重写魔法函数__getitem__,可以视为指定了MyInt在Python内部对应的PyTypeObject对象的tp_as_mapping.mp_subscript操作。
1.3.3 类型的类型
- 在PyTypeObject的顶端部分,有宏Pyobject_VAR_HEAD,说明Python中的类型其实也是对象。
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD PyObject ob_base;
- 类型对象的类型定义在Objects/typeobject.c中的PyType_Type中:
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"type", /* tp_name */
sizeof(PyHeapTypeObject), /* tp_basicsize */
sizeof(PyMemberDef), /* tp_itemsize */
(destructor)type_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)type_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(ternaryfunc)type_call, /* tp_call */
0, /* tp_str */
(getattrofunc)type_getattro, /* tp_getattro */
(setattrofunc)type_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS, /* tp_flags */
type_doc, /* tp_doc */
(traverseproc)type_traverse, /* tp_traverse */
(inquiry)type_clear, /* tp_clear */
0, /* tp_richcompare */
offsetof(PyTypeObject, tp_weaklist), /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
type_methods, /* tp_methods */
type_members, /* tp_members */
type_getsets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
offsetof(PyTypeObject, tp_dict), /* tp_dictoffset */
type_init, /* tp_init */
0, /* tp_alloc */
type_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
(inquiry)type_is_gc, /* tp_is_gc */
};
- PyType_Type 在类型机制中至关重要,所有用户自定义 class 所对应的 PyTypeObject 对象都是通过 PyType_Type创建的,也称为metaclass。
>>> class A:pass
...
>>> A.__class__
<class 'type'>
- 在include/object.h中提供了几个有用的宏,将每一个对象都将自己的引用计数、类型信息初始化:
#define PyObject_HEAD_INIT(type) \
{ _PyObject_EXTRA_INIT \
1, type },
#define PyVarObject_HEAD_INIT(type, size) \
{ PyObject_HEAD_INIT(type) size },
- 这些宏在各种内建类型对象的初始化中被大量使用。 以PyLong_Type为例,在Objects/longobject.c中可以清晰的看到一般的类型对象和PyType_Type之间的关系。
PyTypeObject PyLong_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"int", /* tp_name */
offsetof(PyLongObject, ob_digit), /* tp_basicsize */
sizeof(digit), /* tp_itemsize */
long_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
long_to_decimal_string, /* tp_repr */
&long_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)long_hash, /* tp_hash */
0, /* tp_call */
long_to_decimal_string, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_LONG_SUBCLASS, /* tp_flags */
long_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
long_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
long_methods, /* tp_methods */
0, /* tp_members */
long_getset, /* 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 */
long_new, /* tp_new */
PyObject_Del, /* tp_free */
};
-
运行时图像表现如下:
1.4 对象的多态性
- Python创建对象时,会首先分配内存,进行初始化。
- 之后Python内部会创建一个PyObject*变量来保存和维护这个对象,而不是使用对象变量,比如PyLongObject*。
- 所以在Python内部函数间的是一种范型指针 —— PyObject*。
- 由于无法判断PyObject*所指的指针类型,只能通过所指对象的ob_type域动态判断。
- 而Python也是通过ob_type实现了多态性。
- 根据ob_type指针调用对象的不同,同一个函数可以根据不同的对象有不同的行为,比如:
void Traverse(PyObject* object) { object->ob_type->tp_traverse(object); }
- 如果是longobject则调用:
PyTypeObject PyLong_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) ... ... 0, /* tp_traverse */ ... ...
- 如果是unicodeobject则调用:
PyTypeObject PyUnicodeIter_Type = { ... ... (traverseproc)unicodeiter_traverse, /* tp_traverse */ ... ...
1.5 引用计数
- Python通过管理对象的引用计数ob_refcnt来维护对象在内存中的存在与否,是垃圾收集机制的一部分。
- 通过宏Py_INCREF(op)和Py_DECREF(op)两个宏来增加或减少一个对象的引用计数,当一个对象的引用计数减少到 0 时, Py_DECREF将调用该对象的tp_dealloc来释放对象所占用的内存和系统资源。
#define Py_INCREF(op) ( \
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
((PyObject *)(op))->ob_refcnt++)
#define Py_DECREF(op) \
do { \
PyObject *_py_decref_tmp = (PyObject *)(op); \
if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \
--(_py_decref_tmp)->ob_refcnt != 0) \
_Py_CHECK_REFCNT(_py_decref_tmp) \
else \
_Py_Dealloc(_py_decref_tmp); \
} while (0)
- 在每一个对象创建时,Python提供了_Py_NewReference(op)宏将对象的引用计数初始化为1。
#define _Py_NewReference(op) ( \
_Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA \
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
Py_REFCNT(op) = 1)
- 由于频繁申请和释放内存会大大降低执行效率,因此 Python 中大量采用了内存对象池的技术,使得对象释放的空间归还给内存池而不是直接free,后续使用可先从对象池中获取。