python源码剖析-笔记2
PyStringObject對(duì)象
typedef struct {PyObject_VAR_HEADlong ob_shash;int ob_sstate;char ob_sval[1]; } PyStringObject;頭部保存一個(gè)ob_size,表示字符串在內(nèi)存中的具體長(zhǎng)度,字符串由ob_sval指針指向,但是,字符串的最后一位也一定是’\0’結(jié)束,由于有ob_size的標(biāo)記,允許字符串中間也有’\0’字符的存在。
字符串的類(lèi)型對(duì)象中,tp_itemsize設(shè)置為sizeof(char),這個(gè)值表示變長(zhǎng)對(duì)象中每個(gè)元素單位的長(zhǎng)度,每一個(gè)python中的變長(zhǎng)對(duì)象(string、list等),都需要在對(duì)應(yīng)的類(lèi)型對(duì)象中設(shè)置tp_itemsize的值。tp_itemsize和ob_size共同決定了一個(gè)對(duì)象需要在內(nèi)存中實(shí)際占用的空間大小。
對(duì)于空字符串,python會(huì)返回其內(nèi)部的nullstring對(duì)象,避免每次都創(chuàng)建,這個(gè)對(duì)象相當(dāng)于是共享的。
Intern機(jī)制。為了避免每次為相同的字符串開(kāi)創(chuàng)新對(duì)象,可以利用Intern機(jī)制來(lái)實(shí)現(xiàn)。相當(dāng)于創(chuàng)建時(shí)會(huì)先查找是否有使用了Intern機(jī)制創(chuàng)建的對(duì)象,包含的字符串和待創(chuàng)建的一樣,如果存在,則直接返回該對(duì)象的引用,而不用再次創(chuàng)建一個(gè)全新的對(duì)象。
interned的實(shí)現(xiàn),實(shí)際上是維護(hù)了一個(gè)map<PyObject*, PyObject*>的字典集合,key,value都是那個(gè)采用Interned機(jī)制的Py對(duì)象指針,如果在這個(gè)map中找到一個(gè)對(duì)象的字符串內(nèi)容和待創(chuàng)建的一致,則直接把待創(chuàng)建的指針指向這個(gè)map的value。加入Map時(shí),按照py的引用計(jì)數(shù)規(guī)則,對(duì)應(yīng)的對(duì)象引用計(jì)數(shù)會(huì)先+1,相當(dāng)于這個(gè)對(duì)象引用+2了,這樣的話(huà),這種對(duì)象實(shí)際不可能出現(xiàn)引用為0的情況,所有,對(duì)于加入到Interned中的對(duì)象,會(huì)在加入完畢后執(zhí)行引用計(jì)數(shù)-2的操作。
對(duì)于單個(gè)字符的,也有一個(gè)靜態(tài)緩沖區(qū),類(lèi)似PyIntObject對(duì)小整數(shù)的處理。
PyListObject類(lèi)似于c++中的vector
結(jié)構(gòu)定義:
typedef struct {PyObject_VAR_HEADPyObject **ob_item;int allocated;} PyListObject;一次分配的內(nèi)存是大于現(xiàn)實(shí)需要的內(nèi)存,類(lèi)似vector的內(nèi)存分配策略。
0 <= ob_size <= allocated;
len(list) == ob_size
append的元素會(huì)放在ob_size位置上,所以?xún)?nèi)部元素的內(nèi)存空間不一定連續(xù),但是邏輯上是連續(xù)的
PyList的對(duì)象緩沖池。 在每個(gè)PyList被銷(xiāo)毀的那會(huì),會(huì)檢測(cè)free_list這個(gè)緩沖池是否滿(mǎn)了,如果沒(méi)滿(mǎn),則會(huì)把當(dāng)前這個(gè)待銷(xiāo)毀的對(duì)象放入這個(gè)緩沖池中。當(dāng)然,這個(gè)List內(nèi)部的元素item都是要被free掉內(nèi)存的,不然就是一堆野指針了。只是這個(gè)對(duì)象的內(nèi)存空間會(huì)被緩存下來(lái),避免下次的再次申請(qǐng)內(nèi)存導(dǎo)致額外的消耗。
PyDictObject底層采用hash_table來(lái)存儲(chǔ),hash沖突利用開(kāi)放地址法來(lái)解決(lookdict方法來(lái)搜索元素)。
dict里面每個(gè)pair對(duì)都是一個(gè)PyDictEntry
typedef struct {long me_hash; /* cached hash code of me_key */PyObject *me_key;PyObject *me_value;} PyDictEntry;每個(gè)dict內(nèi)部都有一個(gè)PyDictEntry的小規(guī)模數(shù)組(默認(rèn)8),當(dāng)dict的size小于8時(shí),內(nèi)部標(biāo)識(shí)的ma_table指針就指向這個(gè)小數(shù)組,如果大于8,則申請(qǐng)一塊大內(nèi)存,ma_table指向那塊內(nèi)存。me_key有三種狀態(tài),dummy、unused和實(shí)際有效使用的active。刪除元素后會(huì)變成dummy狀態(tài),freeslot指針會(huì)指向該位置,freeslot在下次插入新元素時(shí)會(huì)使用到。
搜索時(shí)根據(jù)hash值查找,如果查到的key和待搜索的不同,則根據(jù)lookdict二次探測(cè)。
dict每次的插入,會(huì)調(diào)用PyDict_SetItem,在這里面會(huì)先計(jì)算hash值,hash = PyObject_Hash(PyObject*)。在最后,會(huì)根據(jù)裝載率來(lái)決定當(dāng)前ma_table指向的內(nèi)存是否需要擴(kuò)容,因?yàn)檠b載率高了,hash沖突的概率就會(huì)加大。
PyDict使用的緩沖池技術(shù)和Int這些類(lèi)似,都是在一個(gè)Object銷(xiāo)毀時(shí)加入到緩沖池中。
總結(jié)
以上是生活随笔為你收集整理的python源码剖析-笔记2的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c++学习书籍推荐《Advanced C
- 下一篇: python各种类型转换-int,str