首页 > 代码库 > zg手册 之 python2.7.7源码分析(2)-- python 的整数对象和字符串对象
zg手册 之 python2.7.7源码分析(2)-- python 的整数对象和字符串对象
python 中的内置对象
python 中常用的内置对象有:整数对象,字符串对象,列表对象,字典对象。这些对象在python中使用最多,所以在实现上提供缓存机制,以提高运行效率。
整数对象 (PyIntObject)
python 中的整数对象是不可变对象(immutable),即创建了一个 python 整数对象之后,不能再改变该对象的值。
python 为创建整数对象提供了下面三种方法,其中 PyInt_FromString 和 PyInt_FromUnicode 内部也是调用 PyInt_FromLong 创建的整数对象。
PyObject * PyInt_FromLong(long ival); PyObject * PyInt_FromString(char *s, char **pend, int base); PyObject * PyInt_FromUnicode(Py_UNICODE *s, Py_ssize_t length, int base);
下面主要看下 PyInt_FromLong内部的实现
PyObject *PyInt_FromLong(long ival) { register PyIntObject *v; #if NSMALLNEGINTS + NSMALLPOSINTS > 0 // NSMALLNEGINTS = 5,NSMALLPOSINTS = 257 // 如果创建的整数对象值在[-5,256] 则从 small_ints 缓存池中直接返回整数对象 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 // 创建 free_list 缓存列表,提供创建不在 small_ints 缓存池内的对象 if (free_list == NULL) { if ((free_list = fill_free_list()) == NULL) return NULL; } // 从 free_list 中获取新对象 v = free_list; free_list = (PyIntObject *)Py_TYPE(v); // 初始化并返回新的整数对象 PyObject_INIT(v, &PyInt_Type); v->ob_ival = ival; return (PyObject *) v; }
整数对象销毁的操作,仅仅记录释放的内存到 free_list 中
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); }
free_list 与 PyIntBlock 一起管理小整数范围以外的整数对象缓存
struct _intblock { struct _intblock *next; PyIntObject objects[N_INTOBJECTS]; }; typedef struct _intblock PyIntBlock; static PyIntBlock *block_list = NULL; static PyIntObject *free_list = NULL;
整数对象的实现机制总结
small_ints 是小整数对象的缓存池,范围是 [-5,256],可以快速的提供缓存中的对象,仅仅增加对象的引用计数就可以。
free_list 和 block_list 保存创建过的整数对象分配的内存,在创建新的整数对象时,直接从free_list获取对象的内存空间,初始化对象后就可以使用。
python 的整数对象在释放的时候,整数对象占用的内存将继续保存在 block_list 中,并且在 free_list 中记录,将来提供给新创建的整数对象使用。(就是创建整数后分配的内存不会归还给操作系统,所以尽量降低同一时刻分配的整数数量,这样可以降低内存消耗)
字符串对象
python 的字符串对象是变长对象,同时也是不可变对象,字符串不可以修改。
python 内部创建字符串对象的两种方法,PyString_FromStringAndSize 指定了长度。
PyObject * PyString_FromString(const char *str); PyObject * PyString_FromStringAndSize(const char *str, Py_ssize_t size);
看下 PyString_FromString 内部的实现
PyObject *PyString_FromString(const char *str) { register size_t size; register PyStringObject *op; assert(str != NULL); size = strlen(str); if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) { PyErr_SetString(PyExc_OverflowError, "string is too long for a Python string"); return NULL; } // 判断,单字符可以从缓冲中直接返回 if (size == 0 && (op = nullstring) != NULL) { #ifdef COUNT_ALLOCS null_strings++; #endif Py_INCREF(op); return (PyObject *)op; } if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) { #ifdef COUNT_ALLOCS one_strings++; #endif Py_INCREF(op); return (PyObject *)op; } /* Inline PyObject_NewVar */ op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); op->ob_shash = -1; op->ob_sstate = SSTATE_NOT_INTERNED; Py_MEMCPY(op->ob_sval, str, size+1); /* 创建单字符的缓冲 */ if (size == 0) { PyObject *t = (PyObject *)op; PyString_InternInPlace(&t); op = (PyStringObject *)t; nullstring = op; Py_INCREF(op); } else if (size == 1) { PyObject *t = (PyObject *)op; PyString_InternInPlace(&t); op = (PyStringObject *)t; characters[*str & UCHAR_MAX] = op; Py_INCREF(op); } return (PyObject *) op; }
字符缓冲池,这个缓冲池会在第一次创建单字符对象的时候填充,如上面 PyString_FromString 函数内。
#define UCHAR_MAX 0xff static PyStringObject *characters[UCHAR_MAX + 1];
性能相关的 ‘+‘ 操作和 join 操作。每次 ‘+‘ 操作都需要新创建对象,性能较差。join 先计算结果对象的总长度,创建一个结果字符串对象,然后拷贝数据到结果内存位置,所以性能较好。
static PyObject *string_concat(register PyStringObject *a, register PyObject *bb) { // ... op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); op->ob_shash = -1; op->ob_sstate = SSTATE_NOT_INTERNED; Py_MEMCPY(op->ob_sval, a->ob_sval, Py_SIZE(a)); Py_MEMCPY(op->ob_sval + Py_SIZE(a), b->ob_sval, Py_SIZE(b)); op->ob_sval[size] = ‘\0‘; return (PyObject *) op; } static PyObject *string_join(PyStringObject *self, PyObject *orig) { // ... // 计算拼接后字符串总长度 for (i = 0; i < seqlen; i++) { const size_t old_sz = sz; item = PySequence_Fast_GET_ITEM(seq, i); if (!PyString_Check(item)){ #ifdef Py_USING_UNICODE if (PyUnicode_Check(item)) { /* Defer to Unicode join. * CAUTION: There‘s no gurantee that the * original sequence can be iterated over * again, so we must pass seq here. */ PyObject *result; result = PyUnicode_Join((PyObject *)self, seq); Py_DECREF(seq); return result; }#endif PyErr_Format(PyExc_TypeError, "sequence item %zd: expected string," " %.80s found", i, Py_TYPE(item)->tp_name); Py_DECREF(seq); return NULL; } sz += PyString_GET_SIZE(item); if (i != 0) sz += seplen; if (sz < old_sz || sz > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "join() result is too long for a Python string"); Py_DECREF(seq); return NULL; } } // 为拼接后字符串分配空间 res = PyString_FromStringAndSize((char*)NULL, sz); if (res == NULL) { Py_DECREF(seq); return NULL; } // 拷贝拼接字符串到新创建的字符串的内存位置 p = PyString_AS_STRING(res); for (i = 0; i < seqlen; ++i) { size_t n; item = PySequence_Fast_GET_ITEM(seq, i); n = PyString_GET_SIZE(item); Py_MEMCPY(p, PyString_AS_STRING(item), n); p += n; if (i < seqlen - 1) { Py_MEMCPY(p, sep, seplen); p += seplen; } } Py_DECREF(seq); return res;}
字符串对象的实现机制总结
字符串对象实现了单字符的缓冲区,创建单字符的对象时直接从缓冲区中获取对象。
多字符串对象的拼接使用 join 性能好于 ‘+‘。
原文链接: zg手册 之 python2.7.7源码分析(2)-- python 的整数对象和字符串对象