1.PyStringObject
typedef struct {
PyObject_VAR_HEAD
long ob_shash; //存儲字符串hash值,初始為-1
int ob_sstate; //表明該字符串是否經過intend處理
char ob_sval[1];//實際存儲字符串位置,默認為一個字符數組,但創(chuàng)建時會根據size大小擴展此空間
} PyStringObject;
PyString_Type:
類型對象
[stringobject.c]
PyTypeObject PyString_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"str",
sizeof(PyStringObject),
sizeof(char), //tp_itemsize指明變長對象保存的單位長度為一個char
……
(reprfunc)string_repr, /* tp_repr */
&string_as_number, /* tp_as_number */
&string_as_sequence, /* tp_as_sequence */
&string_as_mapping, /* tp_as_mapping */
(hashfunc)string_hash, /* tp_hash */
0, /* tp_call */
……
string_new, /* tp_new */
PyObject_Del, /* tp_free */
};
2.PyStringObject的創(chuàng)建
PyString_FromString:
[stringobject.c]
PyObject* PyString_FromString(const char *str)
{
register size_t size;
register PyStringObject *op;
size = strlen(str); //計算size
if (size > PY_SSIZE_T_MAX) { //判斷字符串長度是否超過范圍
return NULL;
}
if (size == 0 && (op = nullstring) != NULL) { //nullstring對象可能未創(chuàng)建
return (PyObject *)op;
}
if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) { //判斷單字符是否在緩沖區(qū)
return (PyObject *)op;
}
/*創(chuàng)建字符串對象,并初始化*/
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
//根據size大小調整空間
PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
memcpy(op->ob_sval, str, size+1);
……
return (PyObject *) op;
}
字符串對象內存分配
PyString_FromStringAndSize:
和FromString極為相似,只不過需要自行輸入size值,則結尾可以不是NULL
[stringobject.c]
PyObject* PyString_FromStringAndSize(const char *str, int size)
{
register PyStringObject *op;
//處理null string
if(size == 0 && (op = nullstring) != NULL) {
return (PyObject *)op;
}
//處理字符
if(size == 1 && str != NULL && (op = characters[*str & UCHAR_MAX]) !=
NULL)
{
return (PyObject *)op;
}
//創(chuàng)建新的PyStringObject對象,并初始化
//Inline PyObject_NewVar
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
if (str != NULL)
memcpy(op->ob_sval, str, size);
op->ob_sval[size] = '\0';
……
return (PyObject *) op;
}
3.字符串對象的刪除
static void string_dealloc(PyObject *op)
{
switch (PyString_CHECK_INTERNED(op)) {
case SSTATE_NOT_INTERNED:
break;
case SSTATE_INTERNED_MORTAL:
/* revive dead object temporarily for DelItem */
op->ob_refcnt = 3; //在intend字典中該對象引用了兩次,所以需要提前調整引用為3
if (PyDict_DelItem(interned, op) != 0)
Py_FatalError(
"deletion of interned string failed");
break;
case SSTATE_INTERNED_IMMORTAL:
//此狀態(tài)可通過 PyString_InternImmortal(PyObject **p)修改
Py_FatalError("Immortal interned string died.");
default:
Py_FatalError("Inconsistent interned string state.");
}
op->ob_type->tp_free(op);
}
4.Interned實現(xiàn)
interned機制用以避免相同字符串進行多次創(chuàng)建,僅生成一個對象,節(jié)省內存同時使字符串對象做比較時僅比較對象地址是否相同即可,提升了比較效率。實現(xiàn)上通過內部的PyDictObject來存儲該字符串對象,特別注意的是存入字典時,引用計數的細微調整及其緣由。
[stringobjec.c]
void PyString_InternInPlace(PyObject **p)
{
register PyStringObject *s = (PyStringObject *)(*p);
PyObject *t;
if (!PyString_CheckExact(s)) //檢查是否為PyStringObject
return;
if (PyString_CHECK_INTERNED(s))
return;
if (interned == NULL) { //若interned為NULL則初始化一個字典對象
interned = PyDict_New();
}
/* 分析當前字符串對象是否在interned字典中(key),如果存在則增加字典中相同對象的引用,
減少當前字符對象引用*/
t = PyDict_GetItem(interned, (PyObject *)s);
if (t) {
Py_INCREF(t);
Py_DECREF(*p);
*p = t;
return;
}
/*若不在interned字典中則增加至其中*/
PyDict_SetItem(interned, (PyObject *)s, (PyObject *)s);
s->ob_refcnt -= 2; //因為加入到interned字典中時對該對象多引用了2次,需要減少
PyString_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAL; //修改Interned狀態(tài)
}
5.字符串緩沖池
對于單字符情況,python中維護一個PyStringObject * 的數組,在字符創(chuàng)建時會先判定是否在該數組中,如果不在則初始化到該數組中,以便日后使用。雖然Interned提供了相同字符的緩存機制,但每次仍需要針對該字符串進行malloc操作,而對單字符則可直接返回該對象地址,提升效率
緩沖池的聲明:
UCHAR_MAX 該常量和平臺相關
[stringobject.c]
static PyStringObject *characters[UCHAR_MAX + 1];
字符創(chuàng)建時使用緩沖池:
[stringobject.c]
PyObject* PyString_FromStringAndSize(const char *str, int size)
{
register PyStringObject *op;
……
if(size == 1 && str != NULL && (op = characters[*str & UCHAR_MAX]) != NULL)
{
return (PyObject *)op;
}
……
}
緩沖池元素的初始化:
緩沖池元素初始化
[stringobject.c]
PyObject* PyString_FromStringAndSize(const char *str, int size)
{
……
else if (size == 1 && str != NULL)
{
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t); //進行interned
op = (PyStringObject *)t;
characters[*str & UCHAR_MAX] = op; //賦值進緩沖池
Py_INCREF(op); //引用計數增加,避免該緩沖被刪除
}
return (PyObject *) op;
}
6.字符串連接的性能問題
由于字符串對象是不可變對象,所以對字符串進行普通的連接操作(+)時,會迭代的生成很多字符串對象,當待連接字符串數目龐大時,將嚴重影響性能。解決方法是使用字符串對象的join操作,join操作僅需分配一次內存,將序列中的item一次按照分割方法拼湊成一個字符串。
string_concat函數
[stringobject.c]
static PyObject* string_concat(register PyStringObject *a, register
PyObject *bb)
{
register unsigned int size;
register PyStringObject *op;
#define b ((PyStringObject *)bb)
……
//計算字符串連接后的長度size
size = a->ob_size + b->ob_size;
/* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
//將a和b中的字符拷貝到新創(chuàng)建的PyStringObject中
memcpy(op->ob_sval, a->ob_sval, (int) a->ob_size);
memcpy(op->ob_sval + a->ob_size, b->ob_sval, (int) b->ob_size);
op->ob_sval[size] = '\0';
return (PyObject *) op;
#undef b
}
string_join函數
[stringobject.c]
static PyObject* string_join(PyStringObject *self, PyObject *orig)
{
char *sep = PyString_AS_STRING(self);
//假設調用”abc”.join(list),那么self就是“abc”對應的PyStringObject對象
//所以seplen中存儲這“abc”的長度
const int seplen = PyString_GET_SIZE(self);
PyObject *res = NULL;
char *p;
int seqlen = 0;
size_t sz = 0;
int i;
PyObject *seq, *item;
……//獲得list中PyStringObject對象的個數,保存在seqlen中
//遍歷list中每一個字符串,累加獲得連接list中所有字符串后的長度
for (i = 0; i < seqlen; i++)
{
//seq為Python中的list對象,這里獲得其中第i個字符串
item = PySequence_Fast_GET_ITEM(seq, i);
sz += PyString_GET_SIZE(item);
if (i != 0)
sz += seplen;
}
//創(chuàng)建長度為sz的PyStringObject對象
res = PyString_FromStringAndSize((char*)NULL, (int)sz);
//將list中的字符串拷貝到新創(chuàng)建的PyStringObject對象中
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);
memcpy(p, PyString_AS_STRING(item), n);
p += n;
if (i < seqlen - 1)
{
memcpy(p, sep, seplen);
p += seplen;
}
}
return res;
}