内容简介:Python 2.7 源码 - 整数对象
面向对象编程中,对象是数据以及基于这些数据的操作的集合,实际上在计算机中这只是一堆内存逻辑上的集合,无论这段内存是连续的还是分开的。
Python 是由 C 语言写成,描述一段逻辑上结合的内存,直接用结构体 struct
就可以了。但是 struct
并不是面向对象中类型的概念,对象还需要成员函数。所以还需要另外一个结构体 struct
来描述成员函数的集合。
上述特点就导致了在 Python 中,实际的类型也是一个对象,这个类型对象的结构体如下:
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;
cmpfunc tp_compare;
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 */
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;
/* Added in release 2.2 */
/* 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;
#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;
所以在源码中,Python 最基础的对象表示如下:
typedef struct _object
{
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
每一个对象都有一个指针指向自己所属的类型对象,而类型对象则有关于这个对象支持的所有操作的信息。
仔细看 PyTypeObject
的头部, PyObject_VAR_HEAD
即含有 ob_type
,难道还有类型的类型这个概念?是的,这个终极的类型就是元类,即 metaclass
。做个简单的实验。
>>> class A(object): ... pass ... >>> >>> >>> A.__class__ <type 'type'> >>> A.__class__.__class__ <type 'type'> >>> type.__class__ <type 'type'>
在 Python 中类型是对象,所以类型对象也有类型,而元类的类型就是自己。
Python 的整数类型
整数类型没啥可说的,按照 PyTypeObject
结构去填充信息即可:
PyTypeObject PyInt_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"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_to_decimal_string, /* 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_to_decimal_string, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_INT_SUBCLASS, /* 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 */
int_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 */
int_new, /* tp_new */
};
整数对象的内存
再看一眼 PyObject 对象,
typedef struct _object
{
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
我们看到了 ob_refcnt
引用计数对象,可以很容易地联想到 Python 虚拟机是以引用计数为基础构建垃圾回收机制。既然如此,那还有没有必要专门讨论整数对象的内存使用,而直接抽象成引用计数归零后释放内存?
事实上,为了提高虚拟机的性能,整数对象使用了多种技术。
大整数创建
在 intobject.c 中定义有
#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;
block_list
是由一个个 PyIntBlock
串起来的链表,每一个 PyIntBlock
是一个整数数组。 free_list
是由空闲的 PyIntObject
组成的链表,空闲是指这块内存虽然被划分为一个 PyIntObject
,但并没有被用于表示一个真正的整数,即其所存储的信息是无用的。
整数创建时, PyObject * PyInt_FromLong(long ival)
会被调用,
PyObject *
PyInt_FromLong(long ival)
{
register PyIntObject *v;
...
...
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}
static PyIntObject *
fill_free_list(void)
{
PyIntObject *p, *q;
/* Python's object allocator isn't appropriate for large blocks. */
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. */
p = &((PyIntBlock *)p)->objects[0];
q = p + N_INTOBJECTS;
while (--q > p)
Py_TYPE(q) = (struct _typeobject *)(q-1);
Py_TYPE(q) = NULL;
return p + N_INTOBJECTS - 1;
}
当创建整数的时候,会先尝试从 free_list
中取,如果没有空闲的,就会尝试 fill_free_list
。这个新的 PyIntBlock
中,每一个 PyIntObject
都借用 ob_type
来连接成链表。
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
这里只是借用,初看源码的朋友不要被这里搞混了。因为此时这块内存并没有存放整数,它的成员自然可以借来他用。 free_list
指向数组的末尾,从后往前链接到数组首部。
大整数销毁
当一个整数销毁时,便会进入 int_dealloc
函数内。
static void
int_dealloc(PyIntObject *v)
{
if (PyInt_CheckExact(v)) {
Py_TYPE(v) = (struct _typeobject *)free_list;
free_list = v;
}
else
Py_TYPE(v)->tp_free((PyObject *)v);
}
这个函数在正常情况下不会走到 else
分支,意味着所谓的销毁,只是把这个整数的 PyIntObject
重新放回 free_list
链表中,并不会释放这块内存。这岂不会造成内存泄漏?只能说,理论上会。整数对象所占用的内存空间,只和这个程序同时拥有的最多的整数数量有关。
上述做法也是为了优化性能,虚拟机不再需要频繁的 malloc
和 free
。
小整数
除了普通的整数外,Python 中还存在着一种小整数对象。在之前的 PyInt_FromLong
函数中,我们略了一部分,现在我们从另一个角度看。
PyObject *
PyInt_FromLong(long ival)
{
register PyIntObject *v;
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
...
...
}
当想创建的整数在 -NSMALLNEGINTS ~ NSMALLPOSINTS
之间时,就会从 small_ints
数组中直接取出。这范围内的整数即为小整数。小整数使用广泛,循环的初始值,终结值,步进值等等,都是数值很小的整数。小整数从 Python 虚拟机运行之初就存在,使用它们既不需要 malloc
和 free
,甚至连指针操作 free_list
也不要。效率比大整数更高。
而小整数的范围可在编译 Python 时指定,默认为 -5 ~ 257
。
实验
改造一下整数打印函数,反映出内存的变化。
static int values[10];
static int refcounts[10];
/* ARGSUSED */
static int
int_print(PyIntObject *v, FILE *fp, int flags)
/* flags -- not used but required by interface */
{
PyIntObject * intObjectPtr;
PyIntBlock * p = block_list;
PyIntBlock * last = NULL;
int count = 0;
int i = 0;
long int_val = v->ob_ival;
Py_BEGIN_ALLOW_THREADS
fprintf(fp, "%ld", int_val);
Py_END_ALLOW_THREADS
while (p!=NULL)
{
++count;
last = p;
p = p->next;
}
intObjectPtr = last->objects;
intObjectPtr += N_INTOBJECTS-1;
printf("\nvalue's address is @%p\n", v);
for (i = 0; i < 10; i++, --intObjectPtr)
{
values[i] = intObjectPtr->ob_ival;
refcounts[i] = intObjectPtr->ob_refcnt;
}
printf(" value : ");
for (i = 0; i < 8; ++i)
{
printf("%d\t", values[i]);
}
printf("\n");
printf(" refcnt : ");
for (i = 0; i < 8; ++i)
{
printf("%d\t", refcounts[i]);
}
printf("\n");
printf(" block_list count : %d\n", count);
printf(" free_list address : %p\n", free_list);
return 0;
}
运行重新编译后的 python 虚拟机。
大整数实验
首先是连续创建两个大整数。
>>> a=1111 >>> a 1111 value's address is @0x100303888 value : -5 -4 -3 -2 -1 0 1 2 refcnt : 1 1 1 1 54 389 587 84 block_list count : 9 free_list address : 0x1003038a0 >>> b=2222 >>> b 2222 value's address is @0x1003038a0 value : -5 -4 -3 -2 -1 0 1 2 refcnt : 1 1 1 1 54 389 587 84 block_list count : 9 free_list address : 0x1003038b8
第一次的 free_list
,正好是第二次整数的地址。可以看到小整数都至少有一个引用,有些多于一次是因为 python 虚拟机内部使用的缘故。
当尝试创建一个相同的大整数时。
>>> c=2222 >>> c 2222 value's address is @0x1003038b8 value : -5 -4 -3 -2 -1 0 1 2 refcnt : 1 1 1 1 54 389 587 84 block_list count : 9 free_list address : 0x1003038d0
可以看出,虽然值相同,但并不是同一个内存块。
小整数实验
创建两个相同的小整数。
>>> d=1 >>> d 1 value's address is @0x100604ce8 value : -5 -4 -3 -2 -1 0 1 2 refcnt : 1 1 1 1 54 389 591 84 block_list count : 9 free_list address : 0x1003038d0 >>> c=1 >>> c 1 value's address is @0x100604ce8 value : -5 -4 -3 -2 -1 0 1 2 refcnt : 1 1 1 1 54 389 592 84 block_list count : 9 free_list address : 0x100303828
可以看出,整数 1 只是增加了引用计数,内存块是同一个。
以上所述就是小编给大家介绍的《Python 2.7 源码 - 整数对象》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hit Refresh
Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37
Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!