2011年7月28日 星期四

《CPython 源碼剖析》讀書心得 - ch1 - python object

固定的記憶體空間

object 初始化後大小不準變動, 這個限制帶來一個好處: 大小不變表示不可能要求更大的記憶體空間; 不要求更大的記憶體空間表示不可能要求重配一塊區域, 因此也不會改變 address, 所以, 不用擔心之後要更改任何 pointer 的值。

這讓我想到 MySQL InnoDB 的設計採用 clustered index 存資料, clustered index 裡的 index columns = primary key (PK) 接著針對其它 index (non-clustered index), MySQL 改存 PK 的值, 而不是存 data table 的 physical location (MyISAM 的作法)。

這個設計帶來兩個影響

  • 優點: data row 改變位置時 (B-Tree 常分割或合併 node), 不用更新 indexes
  • 缺點: indexes (非 PK) 得先找到 PK, 再用 PK 取資料, 多了一步

不過 MySQL 針對缺點又做了 InnoDB cache, 讓常取的資料能一步就取到資料, 但也增加維護成本, 這是另一個議題了。

PyObject

C 裡沒有 OO 的語法, Python 用 #ifdef 來增加選擇性欄位, 用 #define 擴充欄位並保留同樣的 address offset。所有 object 的 struct 開頭都是

typedef struct {
   PyObject_HEAD
   ...
} PyXObject;

PyObject_HEAD 是巨集, 最簡單的定義如下:

#define PyObject_HEAD \\\\
   int ob_refcnt;
   struct _typeobject *ob_type;

另有 PyObject_VAR_HEAD 幫動態的物件加上 ob_size 欄位:

#define PyObject_VAR_HEAD \\\\
   PyObject_HEAD
   int ob_size;

所以, 最基本的 python object 和含有不等量的 object 定義如下:

typedef struct {
   PyObject_HEAD
} PyObject;

typedef struct {
   PyObject_VAR_HEAD
} PyVarObject;

類別 int 的定義如下:

typedef struct {
   PyObject_HEAD
   long ob_ival;
} PyIntObject;

這個作法漂亮的地方在於, 彈性地定義不同類型物件用到的欄位, 又能讓所有物件有共通的開頭欄位。於是, PyObject* 可以指向任何物件, 並能放心地假設:

  • 第一個欄位是 ob_refcnt
  • 第二個欄位是 ob_type

若用 PyVarObvject*, 則能再假設

  • 第三個欄位是 ob_size

以基本型別為例:

intfloatstringlist
ob_refcntob_refcntob_refcntob_refcnt
ob_typeob_typeob_typeob_type
ob_ivalob_fvalob_sizeob_size
ob_shashob_item
......

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...