投稿007期|令人震驚到發指的PyObject對象代碼設計之美
前言
最近在重溫經典漫畫《SlamDunk》的全國大賽篇,其中的一個情形可以很好的詮釋虎軀一震這個狀態——當櫻木看到流川楓一次高難度投籃時內心的感受:“經過兩萬次射球練習後,櫻木首次明白到流川楓這一球是相當厲害的,那正是他在兩萬次射球練習之中,經常在他腦海中出現的理想射球姿勢”。
言歸正傳,其實對大多數程序開發人員來說,以上這個場景的感慨狀態有時候也出現在我們看到經典代碼的時候。最近正在思考關於Python語言的源生設計機制,有個問題不知道大家是否也有思考過:我們知道Python是由ANSI C實現的,在Python中一切都是對象的概念,但C並不是面向對象的語言,那麽Python中的對象機制是如何實現的呢?帶著這個疑問我研究了Python的源碼,當我看到PyObject這個對象機制的核心結構體時我妥妥的震驚了,那麽借著這期的主題就和大家分享一下PyObject對象基石的設計之美吧!
PyObject結構體介紹
通常來說,無論什麽語言最終被計算機識別到的都是內存中的字節信息,那麽對象實際上就是在更高的層次上把內存上的數據作為一個整體來考慮,這個整體可以是一個整數,可以是一個字符串,也就是我們所理解的對象。Python中所有的東西都是對象,它們擁有一些相同的內容,這些內容定義在PyObject這個結構體中,從Python源碼文件object.h中可以找到它。
typedef struct _object {
PyObject_HEAD
} PyObject;
光看這個結構體可能還看不出什麽高深的設計端倪,因為我們並不知道PyObject_HEAD是什麽?源碼中PyObject_HEAD是一個宏定義,定義了每一個Python對象所占內存的頭部字節內容,那麽我們把PyObject_HEAD這個宏定義替換成具體內容再直觀的看下PyObject這個結構體。
typedef struct _object {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
ob_refcnt是一個整形變量,它的作用是實現引用計數機制。比如一個對象A,當有一個新的PyObject 引用該對象時,A的引用計數增加;而當這個PyObject 被刪除時,A的引用計數減少。當A的引用計數減少到0時,A就可以從堆上被刪除,以釋放出內存供別的對象使用。為什麽是從堆上刪除呢?因為Python中對象是在堆上申請的結構體,這點和C有很大的區別,C的變量是隨函數創建,被壓入棧中的。ob_type是一個指向_typeobject結構體的指針,這個結構體又是什麽東西呢?實際上這個結構體也是一個對象,它是用來指定一個對象類型的類型對象,我們從源碼中可以看出這個類型對象記錄了不同的對象所需的內存空間的大小信息。那麽簡單的說,Python中對象機制的核心一個是引用計數,一個就是類型。
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
cmpfunc tp_compare;
reprfunc tp_repr;
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/* More standard operations (here for binary compatibility) */
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
/* Functions to access object as input/output buffer */
PyBufferProcs *tp_as_buffer;
/* Flags to define presence of optional/expanded features */
long tp_flags;
const char *tp_doc; /* Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
traverseproc tp_traverse;
/* delete references to contained objects */
inquiry tp_clear;
/* Assigned meaning in release 2.1 */
/* rich comparisons */
richcmpfunc tp_richcompare;
/* weak reference enabler */
Py_ssize_t tp_weaklistoffset;
/* Added in release 2.2 */
/* Iterators */
getiterfunc tp_iter;
iternextfunc tp_iternext;
/* Attribute descriptor and subclassing stuff */
struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
descrsetfunc tp_descr_set;
Py_ssize_t tp_dictoffset;
initproc tp_init;
allocfunc tp_alloc;
newfunc tp_new;
freefunc tp_free; /* Low-level free-memory routine */
inquiry tp_is_gc; /* For PyObject_IS_GC */
PyObject *tp_bases;
PyObject *tp_mro; /* method resolution order */
PyObject *tp_cache;
PyObject *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
Py_ssize_t tp_frees;
Py_ssize_t tp_maxalloc;
struct