python 整數底層實現原理

python 整數底層實現原理

python 整數底層實現原理

整數對象在python內部用`PyIntObject`結構體表示

typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;

`PyObject_HEAD`宏中定義的兩個屬性分別是:

int ob_refcnt; 
struct _typeobject *ob_type;

這兩個屬性是所有Python對象固有的:

`ob_refcnt `:對象的引用計數,與Python的內存管理機制有關,它實現了基於引用計數的垃圾收集機制

`ob_type `:用於描述Python對象的類型信息。

由此看來`PyIntObject`就是對c語言當中long類型的擴展。處於對性能的考慮,在python當中對小整數[-5,257)緩存在了`small_ints`(小整數緩存池,一個鏈表),在python的整個生命週期內,只要用到這些整數的地方就不需要再去重新申請內存空間去創建對象了。只需要引用緩存中的對象。

可通過下面的代碼證實


>>> a = 10
>>> b = 10
>>> a is b
True
>>> c = 500
>>> d = 500
>>> c is d
False

`is` 比較的是兩個對象的`id`值是否相同,也就是比較兩個對象是否為同一個實例對象,是否指向同一個內存地址

`==`比較的是兩個對象的內容是否相同,默認會調用對象的`__eq__`方法

a和b都在小整數範圍內,所以他們所指的內存地址相同,c和d超出了小整數範圍內,所以申請了內存去創建新對象,所以所指的內存地址不同

同樣的,對於其他範圍的整數,python也提供了專門的緩衝池,供這些所謂的大整數使用,避免每次使用的時候都要不斷的分配內存。這快存儲空間就是`PyIntBlock`

struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;
static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;

`PyIntBlock`通過一個單向鏈表鏈接在一起,表頭`block_list`始終指向最新創建的`PyIntBlock`對象。`PyIntBlock`有兩個屬性:`next`和`objects`。`next`指向下一個`PyIntBlock`,`objects`用來存儲實際的數據

`free_list`將鏈表中所有`PyIntBlock`的空閒內存。這樣做的好處是當需要一個新的整數,創建內存空間時,可以直接用空閒的內存來存儲

>>> a = 300
>>> def test():
... b = 300
... c = 300
... print(a is b)
... print(c is b)
...
>>> test()
False
True

python出於對性能的考慮,凡是不可變的對象,在同一個代碼塊中,只要值相同的對象,就不會重複創建,而是直接引用已經存在的對象

a和b不是在一個代碼塊中,所以都各自申請了內存空間,存儲在了`free_lsit`中,所以他們的內存空間不同,但是b和c在同一個代碼塊中,當為c賦值時,已經有b存儲在內存中了,所以c並不重新申請內存,而是直接引用了b,所以內存是同一個。

注意:

1. python中小整數是共享的

2. python中的int對象就是c中long類型的擴展

3. 整數對象都是從緩存中獲取的

4. 整數對象回收時,並不會釋放內存。而是將內存加入到`free_list`中。

參考:

https://foofish.net/python_int_implement.html

https://foofish.net/python-int-mystery.html


分享到:


相關文章: