内容简介: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 源码 - 整数对象》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深度学习核心技术与实践
猿辅导研究团队 / 电子工业出版社 / 2018-2 / 119.00元
《深度学习核心技术与实践》主要介绍深度学习的核心算法,以及在计算机视觉、语音识别、自然语言处理中的相关应用。《深度学习核心技术与实践》的作者们都是业界一线的深度学习从业者,所以书中所写内容和业界联系紧密,所涵盖的深度学习相关知识点比较全面。《深度学习核心技术与实践》主要讲解原理,较少贴代码。 《深度学习核心技术与实践》适合深度学习从业人士或者相关研究生作为参考资料,也可以作为入门教程来大致了......一起来看看 《深度学习核心技术与实践》 这本书的介绍吧!