[python]源碼-對象的創建和行為
(明天論文就要送審了!!!距離畢業一個月!!!)
現在還記得剛開始學python時候被這種動態語言驚到的那種感覺,列表和字典對象可以隨意伸縮,簡直不能更帥了,但是一直不知道內部到底是怎麽實現的,python源碼用C實現的,但是C是過程性語言啊。
說怎麽實現之前,先捋捋什麽是對象,對這個我覺得《python源碼剖析》這本書裏面的解釋很有意思:“一個對象實際上就是一片被分配的內存空間,這些內存可能是連續的,也可能是不連續的,這都不重要,重要的是這片內存在更高的層次上可以作為一個整體來考慮,這個整體就是對象,在這片內存中,存儲著一系列的數據以及可以對這些數據進行修改和讀取操作的代碼”,一個很有意思的角度。
一個對象被創建後,它所占用的內存大小就是不可變的了,這意味著對可變長度的數據來說(比如字符串),只能在對象內部維護一個指向可變大小內存區域的指針。
在python中,一切皆是對象,其中所有對象的核心就是一個叫PyObject的結構體
1 typedef struct _object{ 2 int ob_refcnt; 3 struct _typeobject *ob_type; 4 }PyObject;
其中,ob_refcnt為引用計數,_typeobject決定了對象的類型,ob_type指向了一個類型對象(我的理解:基類),這個結構體將出現在所有對象所占有內存的最開始的地方,就比如在一個int類中:
1 typedef struct{ 2 PyObject _HEAD; 3 long ob_ival; 4 }PyIntObject;
int值得信息就保存在ob_ival中,這是python2.5中的int對象,如果是list這種可變長度的對象,這種方法顯然是不行的,那麽就把頭部結構體PyObject中加一個表示此對象占有多少個對象的信息ob_size,這個值也就表明了邊長對象中一共有多少個元素:
1 #define PyObject_VAR_HEAD 2 PyObject_HEAD 3 int ob_size; 4 5 typedef struct{ 6 PyObject_VAR_HEAD7 }PyVarObject;
所以,python中對象的內存表示是下面這個樣子的:
從上面可以看到,對象的類型完全由ob_type指向的類型對象決定的,那麽類型對象是什麽鬼?
typedef struct _typedefobject{ PyObject_VAR_HEAD char*tp_name; int tp_basicsize,tp_itemsize; destructor tp_dealloc; printfunc tp_print; hashfunc tp_hash; ternaryfunc tp_call; /*……*/ }PyTypeObject;
*tp_basicsize,tp_itemsize指定了分配內存空間的大小
*其余的帶有func字眼的指向函數的指針表明了這個對象攜帶的操作
那麽,對象是怎麽創建的?
首先要說明在python新式類中,所有的對象的基類都是object,就像java中所有對象的基類都是Object所構成的單根類體系一樣,也就是說,所有的對象都會自動繼承其基類的所有方法。
在需要創建一個對象的時候,比如創建int對象需要用到的PyInt_Type對象時候,先尋找PyInt_Type類中的tp_new指針,如果這個指針為null,就尋找其父類,比如object中的tp_new指針,用這個指針指向的函數來創建對象,它指向的函數會首先查看PyInt_Type中的tp_basicsize字段,根據這個字段的信息來決定為這個對象分配多少內存,當創建完成後,會在PyInt_Type中尋找tp_init,用其指向的函數來初始化tp_new分配的內存。我理解的是,tp_new就相當於python類中的__new__靜態類方法,tp_init就相當於__init__方法。
可以看到,在PyTypeObject中有很多只想函數的指針,這些函數指針也就決定了對象的行為,其中有三個非常重要的函數指針指向三個操作族:
tp_as_number-------->PyNumberMethods
tp_as_sequence------>PySequenceMethods (列表list)
tp_as_mapping------>PyMappingMethods (字典dict)
對每一種對象來說,可以同時指向三種操作族,可以通過哪些鉤子方法實現,比如__getitem__,__setitem__
參考資料:python源碼剖析
[python]源碼-對象的創建和行為