成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Hack Python 整數(shù)對(duì)象

sixgo / 1120人閱讀

摘要:在類(lèi)型系統(tǒng)部分中定義如下類(lèi)型表示對(duì)象,如對(duì)象表示例化后的類(lèi)型類(lèi)型初始化函數(shù)表示在初始化時(shí)調(diào)用的用來(lái)初始化類(lèi)型的函數(shù),如構(gòu)造函數(shù)表示構(gòu)造對(duì)象需要的函數(shù),如。

背景

寫(xiě)這篇文章的原因是目前在看《Python源碼剖析》[1],但是這本書(shū)的作者陳儒老師剖析源碼的目的好像不是太明確,所以看上去是為了剖析源碼而剖析源碼,導(dǎo)致的結(jié)果是這本書(shū)里面的分析思路不太清楚(可能是我的理解問(wèn)題),而且驗(yàn)證想法的方式是把變量值打印出來(lái),當(dāng)然這是種很好的方式,但使用調(diào)試工具顯然更好一點(diǎn)。我讀這本書(shū)和看源碼的目的很簡(jiǎn)單:為了理解計(jì)算機(jī)的運(yùn)行,理解大型軟件工程的設(shè)計(jì)。正如文章的題目為hack python而不是源碼閱讀,hack是一個(gè)理性的分析過(guò)程,而閱讀很多時(shí)候隨心所欲的成分多一些。但總體的過(guò)程還是按照書(shū)中的順序來(lái)的,這本書(shū)很明確的一點(diǎn)就是要做什么不要做什么,這一點(diǎn)我很喜歡??赡軙?huì)是一個(gè)系列,也可能只有這一篇,并不算挖坑。我更希望從多種視角來(lái)審視Python作為一門(mén)動(dòng)態(tài)語(yǔ)言的各種特性。作為一個(gè)還沒(méi)有學(xué)過(guò)編譯原理的人來(lái)說(shuō)這個(gè)目標(biāo)顯然很難完成,但正是難完成的東西,才有完成的意義。這篇文章的源碼均來(lái)自Python-2.5.6[2],所有分析也都是基于此,編譯環(huán)境是由Koding[3]提供的,還會(huì)用到gdb[4]作為調(diào)試工具。

概要

這篇文章主要從源碼和運(yùn)行時(shí)的角度觀察Python的整形結(jié)構(gòu)。

數(shù)據(jù)結(jié)構(gòu)

先來(lái)看一下PyIntObject的聲明[5]:

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

可以看到PyIntObject被聲明為一個(gè)結(jié)構(gòu)體,包括了Python對(duì)象元信息 和一個(gè)C語(yǔ)言的long型整數(shù)。而Python的Python對(duì)象元信息是什么呢?這個(gè)問(wèn)題牽扯到C語(yǔ)言中的宏[6]和Python類(lèi)型系統(tǒng)的本質(zhì)[f],先按下不表。

封裝了C語(yǔ)言long型整數(shù)的PyIntObject作為數(shù)據(jù)結(jié)構(gòu)并沒(méi)有什么能讓人心潮澎湃的地方,它的迷人之處在于算法[7],也就是PyIntObject的動(dòng)態(tài)組織方式,可是我不可能僅從PyIntObject上管窺到它的組織方式,需要更多的信息來(lái)達(dá)成這個(gè)目的。再來(lái)看源碼:

#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];
};

typedef struct _intblock PyIntBlock;

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

這段代碼對(duì)于PyIntObject的組織方式已經(jīng)說(shuō)得很清楚了,不用解釋。下圖形象一點(diǎn):

正如前面所說(shuō)的,這個(gè)鏈表式的數(shù)據(jù)結(jié)構(gòu)還是實(shí)在太簡(jiǎn)單,沒(méi)多少值得把玩的地方。假設(shè)我是Python的作者,我會(huì)想首先想這門(mén)語(yǔ)言出現(xiàn)的原因,一定是不爽于現(xiàn)有的某些方案,所以才要自己創(chuàng)造新的方案,Python被創(chuàng)造為一種動(dòng)態(tài)類(lèi)型語(yǔ)言,相比于C之類(lèi)的靜態(tài)語(yǔ)言?xún)?yōu)勢(shì)在于“動(dòng)態(tài)”二字。但動(dòng)態(tài)不是簡(jiǎn)單的聲明和組織幾個(gè)數(shù)據(jù)結(jié)構(gòu)就完事,需要被貫穿到這門(mén)語(yǔ)言運(yùn)行的始終。

運(yùn)行時(shí)狀態(tài)

下面來(lái)看一下運(yùn)行時(shí)狀態(tài),根據(jù)函數(shù)名可以肯定的是fill_free_list這個(gè)函數(shù)必然會(huì)在很早的時(shí)候被調(diào)用(來(lái)準(zhǔn)備需要的內(nèi)存),我們先不關(guān)注它到底是怎么做內(nèi)存分配的,先下個(gè)斷點(diǎn),看一下誰(shuí)第一個(gè)調(diào)用它,看到第一個(gè)觸發(fā)斷點(diǎn)的地方是_PyInt_Init,也就是Python整型對(duì)象(類(lèi)型對(duì)象)的初始化函數(shù),推測(cè)應(yīng)該是Python中的每一個(gè)類(lèi)型對(duì)象都會(huì)有一個(gè)初始化函數(shù),在Python開(kāi)始運(yùn)行時(shí)完成初始化工作。來(lái)看這個(gè)_PyInt_Init函數(shù)具體包含了什么內(nèi)容:

int
_PyInt_Init(void)
{
    PyIntObject *v;
    int ival;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {
              if (!free_list && (free_list = fill_free_list()) == NULL)
            return 0;
        /* PyObject_New is inlined */
        v = free_list;
        free_list = (PyIntObject *)v->ob_type;
        PyObject_INIT(v, &PyInt_Type);
        v->ob_ival = ival;
        small_ints[ival + NSMALLNEGINTS] = v;
    }
#endif
    return 1;
}

首先,正常情況下(排除內(nèi)存不夠),free* 類(lèi)似命名的函數(shù)的返回值不會(huì)是NULL,所以直接忽略掉for循環(huán)中的if,在其下設(shè)一個(gè)斷點(diǎn)觀察free_list此時(shí)的值(被賦值之前或直接觀察v的值),因?yàn)檫@是全局變量被賦值,記錄一下它之前的值,說(shuō)不定以后有用。

再往下看,除了PyObject_INIT函數(shù)(我們先不管它,等HACK Python類(lèi)型系統(tǒng)[f]的時(shí)候再研究),還有small_ints這個(gè)奇葩數(shù)組,根據(jù)名字,這是個(gè)在Python整型對(duì)象中必然會(huì)用到的東西,所以逃不掉了,不過(guò)還好,不就是個(gè)數(shù)組嘛!

數(shù)據(jù)結(jié)構(gòu):small_ints

我們往上找這個(gè)small_ints數(shù)組的聲明,看看他究竟暗藏了什么玄機(jī)。

static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

發(fā)現(xiàn)了這一句,實(shí)在是太簡(jiǎn)單了,一個(gè)PyIntObject指針數(shù)組。大概長(zhǎng)這個(gè)樣子:

同時(shí)還發(fā)現(xiàn)了剛才不知道的宏,早就猜中的東西,現(xiàn)在是多少也無(wú)關(guān)緊要了??墒沁@個(gè)small_ints到底是用來(lái)干嘛的還不清楚,僅僅知道它是什么永遠(yuǎn)不好玩兒,為什么才是真正需要關(guān)注的??墒?,怎么求出這個(gè)問(wèn)題的答案呢?問(wèn)源碼作者最直接了,可是時(shí)效性太差,放棄;上網(wǎng)搜,太沒(méi)挑戰(zhàn),放棄;還有源碼,不知道可不可以,要回答的問(wèn)題是為什么,比如我為什么需要一臺(tái)電腦呢?回答是因?yàn)槲以谂艹绦虻臅r(shí)候要用。現(xiàn)在再來(lái)看一下_PyInt_Init對(duì)數(shù)組small_ints做了什么。

過(guò)程:_PyInt_Init

可以看到的是small_ints完全是一個(gè)靜態(tài)的結(jié)構(gòu),它是在_PyInt_Init被調(diào)用也就是系統(tǒng)初始化時(shí)就被直接分配了_intblock塊,當(dāng)然按照_intblock塊的大小,N_INTOBJECTS為*((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject)),這是多少呢?還需要知道sizeof(PyIntObject) ,用gdb看看到這樣:

所以一個(gè)_intblock可以容納41個(gè)PyIntObject,比small_ints的size還小(所以下面的圖有問(wèn)題,不過(guò)這個(gè)信息不怎么重要,因?yàn)榭梢愿膕mall_ints的相關(guān)宏的值,讓圖變得正確)。反正在_PyInt_Init中,只要空間不夠(free_list == NULL,if條件&&左值),就調(diào)用fill_free_list分配_intblock。按照默認(rèn)的參數(shù),大概得分配7個(gè)_intblock來(lái)完成_PyInt_Init(同樣,因?yàn)橐揽繀?shù),不重要)。

那現(xiàn)在,初始化過(guò)程已經(jīng)完成了,我們總結(jié)一下,_PyInt_Init的主要作用就是構(gòu)建一個(gè)small_ints及其空間(在《Python源碼剖析》用小整數(shù)池來(lái)描述,我覺(jué)得這么多概念容易confuse,所以直接把本質(zhì)說(shuō)一下就好),但里面并沒(méi)有足夠的信息來(lái)判斷small_ints及其空間是如何被利用的,問(wèn)題(為什么需要small_ints?)依然沒(méi)有被解決。_PyInt_Init這條線索雖然斷了,但好在還有PyInt_FromLong。

過(guò)程:PyInt_FromLong

注意到Python在這個(gè)時(shí)候已經(jīng)經(jīng)歷了各種復(fù)雜的初始化過(guò)程,打印出了它的版本信息,萬(wàn)事俱備,只欠輸入。不關(guān)注輸入過(guò)程或者調(diào)用信息,假設(shè)現(xiàn)在就調(diào)用了PyInt_FromLong。

PyObject *
PyInt_FromLong(long ival)
{
    register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    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
    if (free_list == NULL) {
        if ((free_list = fill_free_list()) == NULL)
            return NULL;
    }
    /* Inline PyObject_New */
    v = free_list;
    free_list = (PyIntObject *)v->ob_type;
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}

構(gòu)造一個(gè)Python整數(shù)對(duì)象需要一個(gè)long型整數(shù),如果這個(gè)long型整數(shù)大小是在-NSMALLNEGINTS到NSMALLPOSINTS之間,就認(rèn)為它是一個(gè)小整數(shù),在small_ints空間中找到封裝該小整數(shù)的PyIntObject并調(diào)用Py_INCREF方法。這里通過(guò)命名可以知道Py_INCREF方法的作用是對(duì)對(duì)象的引用數(shù)做自增操作,具體實(shí)現(xiàn)不深入。

當(dāng)然上面只是針對(duì)小整數(shù)的情況,大整數(shù)是怎樣處理的呢?繼續(xù)往下看就可以知道。過(guò)程跟_PyInt_Init中一樣,一樣的通過(guò)判斷條件語(yǔ)句的右值來(lái)調(diào)用fill_free_list方法。

其實(shí)大整數(shù)對(duì)象和小整數(shù)對(duì)象的區(qū)別就在于:
1. 小整數(shù)對(duì)象是在系統(tǒng)初始化的時(shí)候就為其分配了內(nèi)存空間PyIntBlock(也就是 _intblock),并寫(xiě)入值,而對(duì)于大整數(shù)如果現(xiàn)有的之前分配好的PyIntBlock中有空間沒(méi)用完的話就直接把值寫(xiě)入該塊(當(dāng)然寫(xiě)之前還要移動(dòng)free_list并對(duì)對(duì)象做初始化操作),如果用完了就調(diào)用fill_free_list新建PyIntBlock。
2. 當(dāng)要用一個(gè)小整數(shù)來(lái)構(gòu)造小整數(shù)對(duì)象時(shí),只對(duì)其相應(yīng)的引用計(jì)數(shù)器做自增操作,而不像大整數(shù)那樣做復(fù)雜的函數(shù)調(diào)用和內(nèi)存分配操作,目的當(dāng)然是時(shí)間效率,典型的那空間換時(shí)間的做法。
3. 本質(zhì)上二者在內(nèi)存中沒(méi)有任何區(qū)別,小整數(shù)和大整數(shù)的界限可以當(dāng)作參數(shù)來(lái)自己配置也可以說(shuō)明這一點(diǎn),不過(guò)這個(gè)界限究竟設(shè)為多少Python的效率能達(dá)到做好的平衡呢?不知道默認(rèn)的參數(shù)設(shè)置成那樣的原因是什么,有沒(méi)有更加科學(xué)的參數(shù)?

后記

作為第一篇關(guān)于Hack Python的文章,里面有很多東西都比較啰嗦。要做的是還原整個(gè)探索的過(guò)程,包括所有走過(guò)的彎路,尤其要關(guān)注的是為什么,而不僅僅著眼于是什么。

對(duì)于Python類(lèi)型系統(tǒng)的探索需要明確以下幾點(diǎn):

概念:對(duì)于概念基本原則是越少定義越好,因?yàn)楹芏鄸|西本質(zhì)上都是一回事,但是一些基本的約定還是很重要的,可以避免每次都重復(fù)啰嗦。在類(lèi)型系統(tǒng)部分中定義如下:類(lèi)型表示PyXXXObject對(duì)象,如PyIntObject;對(duì)象表示例化后的類(lèi)型;類(lèi)型初始化函數(shù)表示在Python初始化時(shí)調(diào)用的用來(lái)初始化類(lèi)型的函數(shù),如PyInt_Init;構(gòu)造函數(shù)表示構(gòu)造對(duì)象需要的函數(shù),如PyInt_FromLong。

研究范圍:在以后的hack對(duì)象系統(tǒng)中,默認(rèn)只研究關(guān)于本類(lèi)型的內(nèi)容,對(duì)于整個(gè)類(lèi)型系統(tǒng)的宏觀概覽不涉及;除非用于比較,其他類(lèi)型不涉及;與C語(yǔ)言相關(guān)的基本概念不涉及,只給出資料;與研究工具相關(guān)的步驟不涉及,只給出結(jié)果和基本參考資料。主要目的在于著眼于每種類(lèi)型,在研究完所有類(lèi)型后再總結(jié)整個(gè)類(lèi)型系統(tǒng)。

對(duì)于類(lèi)型系統(tǒng)的研究由本文可以得出以下順序:類(lèi)型基本的數(shù)據(jù)結(jié)構(gòu)-基本類(lèi)型數(shù)據(jù)結(jié)構(gòu)的組織-類(lèi)型特殊過(guò)程分析和解讀-細(xì)節(jié)-總結(jié)。


文章里面包含鏈接有礙于流暢閱讀,所以取消文章內(nèi)的鏈接,在末尾加參考資料部分以示引用或概念解釋。


資料:

[1]《python源碼剖析》

[2]Python-2.5.6

[3]Koding

[4]GDB

[5]聲明

[6]宏

[7]Python的類(lèi)型系統(tǒng)總結(jié)


延伸:

使用gdb調(diào)試運(yùn)行時(shí)的程序小技巧

【轉(zhuǎn)載請(qǐng)注明出處 dukeyunz.com】

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/45383.html

相關(guān)文章

  • 瀏覽器兼容你知多少?

    摘要:在各大瀏覽器廠商的發(fā)展過(guò)程中,它們對(duì)的標(biāo)準(zhǔn)各有不同的實(shí)現(xiàn),標(biāo)準(zhǔn)不同存在差異所以產(chǎn)生兼容性的問(wèn)題。它是一種對(duì)特定的瀏覽器或?yàn)g覽器組顯示或隱藏規(guī)則或聲明的方法。但是及更低版本瀏覽器會(huì)繼續(xù)解析。 為什么會(huì)存在瀏覽器兼容問(wèn)題? 首先要了解兼容,我們先得了解一下為什么會(huì)存在瀏覽器兼容問(wèn)題。在各大瀏覽器廠商的發(fā)展過(guò)程中,它們對(duì)web的標(biāo)準(zhǔn)各有不同的實(shí)現(xiàn),標(biāo)準(zhǔn)不同存在差異所以產(chǎn)生兼容性的問(wèn)題。 瀏覽...

    txgcwm 評(píng)論0 收藏0
  • ES5學(xué)習(xí)(上)

    摘要:對(duì)象是一個(gè)值超出有效范圍時(shí)發(fā)生的錯(cuò)誤。包括返回原數(shù)組包括數(shù)組對(duì)象函數(shù)可以用來(lái)判斷變量是否為對(duì)象數(shù)組對(duì)象函數(shù)構(gòu)造函數(shù)與直接賦值是等價(jià)的。只適用于,數(shù)組不適用通過(guò)可以看出一個(gè)值到底是什么類(lèi)型,其中返回值的第二個(gè)值表示該值的構(gòu)造函數(shù)。 這是ES5的入門(mén)篇教程的筆記,網(wǎng)址:JavaScript教程,以下內(nèi)容中黑體表示大標(biāo)題,還有一些重點(diǎn);斜體表示對(duì)于自身,還需要下功夫?qū)W習(xí)的內(nèi)容。這里面有一些自...

    HackerShell 評(píng)論0 收藏0
  • JavaScript 轉(zhuǎn)換數(shù)字為整數(shù)的方法

    摘要:另外自己寫(xiě)代碼測(cè)試了下和的速度,比較結(jié)果如下位操作轉(zhuǎn)換整數(shù)的原理參考上面對(duì)于位操作的說(shuō)明,點(diǎn)擊下面鏈接有這樣一段話中,數(shù)字存儲(chǔ)是雙進(jìn)度位浮點(diǎn)數(shù)。但是位操作卻會(huì)把要操作的運(yùn)算元當(dāng)做位帶符號(hào)的整數(shù)。因此進(jìn)行位操作時(shí),會(huì)自動(dòng)把數(shù)字先轉(zhuǎn)換為整數(shù)。 本文將會(huì)列舉并說(shuō)明JavaScript 把一個(gè)number(或者numerical的對(duì)象)轉(zhuǎn)換成一個(gè)整數(shù)相關(guān)方法。 使用parseInt parse...

    YanceyOfficial 評(píng)論0 收藏0
  • Python整數(shù)對(duì)象池:“內(nèi)存泄漏”?

    摘要:這里需要說(shuō)明的是,小的整數(shù)對(duì)象,將全部直接放置于內(nèi)存中。內(nèi)存泄漏上述的機(jī)制可以很好減輕的問(wèn)題,同時(shí)可以根據(jù)所跑的程序不同的特點(diǎn)來(lái)做從而編譯出自己認(rèn)為合適的。 墻上的斑點(diǎn) 我第一次注意到短褲上的那個(gè)破洞,大概是在金年的三月上旬。如果想要知道具體的時(shí)間,那就得回想一下當(dāng)時(shí)我看見(jiàn)的東西。我還能夠回憶起,游泳池頂上,搖曳的、白色的燈光不停地映在我的短褲上;有三五名少年一同扎進(jìn)了水里。哦,那是大...

    isLishude 評(píng)論0 收藏0
  • 9:瀏覽器兼容

    摘要:由和開(kāi)發(fā)的瀏覽器排版引擎,年月發(fā)布。瀏覽器的兼容問(wèn)題是我們必須去克服的。表示過(guò)濾器的意思,它是一種對(duì)特定的瀏覽器或?yàn)g覽器組顯示或隱藏規(guī)則或聲明的方法。? 9:瀏覽器兼容 常見(jiàn)的主流瀏覽器 1)主流瀏覽器 Internet Explorer、 Safari、Mozilla Firefox、 Google Chrome、Opera、百度、360、搜狗、傲游 2)最早的瀏覽器 : Mosaic ...

    AWang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<