Python源碼學(xué)習(xí)筆記 2 整數(shù)對象

Python中的整數(shù)類型是不可變對象肠阱,為了提高python運行效率饥臂,內(nèi)部實現(xiàn)了小整數(shù)對象池(數(shù)組實現(xiàn)),和通用整數(shù)緩沖池(單鏈表實現(xiàn))茅信。小整數(shù)是可以復(fù)用的盾舌,而通用整數(shù)是即使數(shù)值相同也會創(chuàng)建兩個不同的整數(shù)對象。

1.PyIntObject

該結(jié)構(gòu)僅適用Python2版本蘸鲸,該版本下數(shù)字長度大于long型時妖谴,對象類型會轉(zhuǎn)變?yōu)镻yLongObject

PyIntObject結(jié)構(gòu):

[intobject.h]
typedef struct {
    PyObject_HEAD //標(biāo)準(zhǔn)PyObject頭
    long ob_ival; //實際存儲數(shù)值得變量
} PyIntObject;

整數(shù)對象的創(chuàng)建:
提供了三種創(chuàng)建方式,分類為long型創(chuàng)建或從字符串創(chuàng)建酌摇,但從字符串創(chuàng)建最終還是需要調(diào)用long型創(chuàng)建方式

 PyObject *PyInt_FromLong(long ival) //從long整數(shù)創(chuàng)建
 PyObject* PyInt_FromString(char *s, char **pend, int base) //從ASCII字符串創(chuàng)建
#ifdef Py_USING_UNICODE 
  PyObject*PyInt_FromUnicode(Py_UNICODE *s, int length, int base) //從Unicode字符串創(chuàng)建
#endif

整數(shù)對象相減:

[intobject.h]
#define PyInt_AS_LONG(op) (((PyIntObject *)(op))->ob_ival)
//宏膝舅,犧牲類型安全,換取執(zhí)行效率

[intobject.c]
#define CONVERT_TO_LONG(obj, lng)       \
    if (PyInt_Check(obj)) {         \
        lng = PyInt_AS_LONG(obj);   \
    }                   \
    else {                  \
        Py_INCREF(Py_NotImplemented);   \
        return Py_NotImplemented;   \
    }
  
static PyObject *
int_sub(PyIntObject *v, PyIntObject *w)
{
    register long a, b, x;
    CONVERT_TO_LONG(v, a);
    CONVERT_TO_LONG(w, b);
    x = a - b;
    if ((x^a) >= 0 || (x^~b) >= 0) //溢出檢查
        return PyInt_FromLong(x);//此處印證了Python中整數(shù)對象是一個不可變對象
    return PyLong_Type.tp_as_number->nb_subtract((PyObject *)v,
                             (PyObject *)w); //若溢出會將返回一個PyLongObject
}

2.小整數(shù)對象

在編程中小整數(shù)對象使用頻率很高(如循環(huán)標(biāo)記)窑多,而從上文中我們已經(jīng)得知Python中整數(shù)對象是不可變對對象仍稀,若沒有特殊的方法處理,會導(dǎo)致頻繁創(chuàng)建小整數(shù)對象埂息,嚴(yán)重影響運行效率和浪費內(nèi)存技潘。
在python內(nèi)部通過創(chuàng)建一個PyIntObject *的數(shù)組small_ints來優(yōu)化這個情況, 數(shù)組默認(rèn)范圍為[-5,257)

[intobject.c]
#ifndef NSMALLPOSINTS
     #define NSMALLPOSINTS       257 //正數(shù)
#endif
#ifndef NSMALLNEGINTS
     #define NSMALLNEGINTS       5 //負(fù)數(shù)
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
     static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif

3.通用整數(shù)對象

對于小整數(shù)以外的其他整數(shù)遥巴,python內(nèi)部使用多個“預(yù)先”分配的內(nèi)存塊來緩存這些整數(shù)對象,每一個內(nèi)存塊稱為一個block_list, 未使用的空間使用free_list穿起來

[intobject.c]

#define BLOCK_SIZE  1000    /* 1K less typical malloc overhead */
#define BHEAD_SIZE  8   /* Enough for a 64-bit pointer */
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))

struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INTOBJECTS]; //PyIntObject數(shù)組享幽,用以存儲整數(shù)
};

typedef struct _intblock PyIntBlock;

static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;

4.整數(shù)對象的創(chuàng)建與刪除

整數(shù)對象的創(chuàng)建

 [intobject.c]
PyObject* PyInt_FromLong(long ival)
{
     register PyIntObject *v; 
#if NSMALLNEGINTS + NSMALLPOSINTS > 0  
//判斷小整數(shù)對象池是否激活
     if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { //嘗試使用小整數(shù)對象池
        v = small_ints[ival + NSMALLNEGINTS];
        Py_INCREF(v);
        return (PyObject *) v;
    }
#endif
    if (free_list == NULL) {//當(dāng)前block_list無空閑铲掐,需要申請新的PyIntBlock
        if ((free_list = fill_free_list()) == NULL) 
            return NULL;
    }
    //使用通用整數(shù)對象池
    v = free_list;
    free_list = (PyIntObject *)v->ob_type;
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}

整數(shù)對象的刪除:
對整數(shù)對象進行刪除時,實際上是將該對象加入到free_list鏈表中值桩,以便整型對象的緩沖摆霉,避免頻繁的malloc操作

static void
int_dealloc(PyIntObject *v)
{
    if (PyInt_CheckExact(v)) {
        /*將該整型對象加入到free_list鏈表中*/
        Py_TYPE(v) = (struct _typeobject *)free_list;
        free_list = v;
    }
    else
        Py_TYPE(v)->tp_free((PyObject *)v);//若為整型派生對象則僅調(diào)用指定的tp_free()
}

小整數(shù)對象池的初始化:
在Python虛擬機初始化時_PyInt_Init會被調(diào)用,創(chuàng)建小整數(shù)緩沖池

int
_PyInt_Init(void)
{
    PyIntObject *v;
    int ival;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    /* 小整數(shù)對象仍在block_list中創(chuàng)建奔坟,只是將創(chuàng)建后的對象地址存入對應(yīng)位置small_ints中 */
    for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {
        if (!free_list && (free_list = fill_free_list()) == NULL)
            return 0;
        v = free_list;
        free_list = (PyIntObject *)Py_TYPE(v);
        (void)PyObject_INIT(v, &PyInt_Type);
        v->ob_ival = ival;
        small_ints[ival + NSMALLNEGINTS] = v;//指針存入small_ints
    }
#endif
    return 1;
}

fill_free_list實現(xiàn):
當(dāng)free_list為NULL時創(chuàng)建新的整數(shù)對象携栋,會觸發(fā)fill_free_list操作,將malloc一個PyIntBlock蛀蜜,并將內(nèi)部的object數(shù)組中每個整數(shù)對象刻两,通過ob_type作為后繼(犧牲掉類型安全)形成單鏈表關(guān)系

[object.h]
#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)

[intobject.c]
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的next鏈接到舊block_list上
    block_list = (PyIntBlock *)p; //另新block為block_list頭結(jié)點
    p = &((PyIntBlock *)p)->objects[0];
    q = p + N_INTOBJECTS;
    while (--q > p)
        Py_TYPE(q) = (struct _typeobject *)(q-1); //創(chuàng)建block內(nèi)部空閑整數(shù)對象間連接關(guān)系
    Py_TYPE(q) = NULL;
    return p + N_INTOBJECTS - 1;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市滴某,隨后出現(xiàn)的幾起案子磅摹,更是在濱河造成了極大的恐慌,老刑警劉巖霎奢,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件户誓,死亡現(xiàn)場離奇詭異,居然都是意外死亡幕侠,警方通過查閱死者的電腦和手機帝美,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晤硕,“玉大人悼潭,你說我怎么就攤上這事∥韫浚” “怎么了舰褪?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疏橄。 經(jīng)常有香客問我占拍,道長,這世上最難降的妖魔是什么捎迫? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任晃酒,我火速辦了婚禮,結(jié)果婚禮上窄绒,老公的妹妹穿的比我還像新娘贝次。我一直安慰自己,他們只是感情好彰导,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布浊闪。 她就那樣靜靜地躺著恼布,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搁宾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天倔幼,我揣著相機與錄音盖腿,去河邊找鬼。 笑死损同,一個胖子當(dāng)著我的面吹牛翩腐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膏燃,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼茂卦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了组哩?” 一聲冷哼從身側(cè)響起等龙,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伶贰,沒想到半個月后蛛砰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡黍衙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年泥畅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琅翻。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡位仁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出方椎,到底是詐尸還是另有隱情聂抢,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布辩尊,位于F島的核電站涛浙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏摄欲。R本人自食惡果不足惜轿亮,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胸墙。 院中可真熱鬧我注,春花似錦、人聲如沸迟隅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至奔缠,卻和暖如春掠抬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背校哎。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工两波, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闷哆。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓腰奋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抱怔。 傳聞我的和親對象是個殘疾皇子劣坊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Python源碼剖析筆記2-Python整數(shù)對象 千里之行始于足下,從簡單的類別開始分析屈留,由淺入深也不至于自己喪失...
    __七把刀__閱讀 1,861評論 5 2
  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫完項目接著寫寫一名3年工作經(jīng)驗的J...
    燕京博士閱讀 7,575評論 1 118
  • ? 1.python的整型對象是PyIntObject對象局冰,這個對象是一個不可變對象,即沒有ob_size這個變量...
    qalangtao閱讀 528評論 0 0
  • 今晚行動戰(zhàn)隊小伙伴們分享周年慶廈門站活動绕沈,真的真的炒雞贊锐想! 剛開始的時候覺得這場活動與我沒多大關(guān)系,因為我去不了乍狐,...
    萵是秀秀閱讀 197評論 0 0
  • 2017年8月31日身修家和 美麗中國李瓊 種子踐行日記 第 9 天??服務(wù)眾生: 將訂閱的文章分享到各個群...
    江南木子閱讀 148評論 0 0