日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python底层源码_python源码剖析——系列一

發(fā)布時(shí)間:2023/12/10 python 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python底层源码_python源码剖析——系列一 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

0 前言

Python 非常好用,哪怕一個(gè)沒(méi)上過(guò)匯編,操作系統(tǒng),編譯原理等一系列基礎(chǔ)計(jì)算機(jī)課程的人,也能快速上手。

再拿反面教材C++舉例,引用和指針的區(qū)別入門階段就搞懵了一批人。而指針和引用如果拓展開(kāi),C++老司機(jī)也是很容易翻車的。

Python好用的一個(gè)原因,就是把底層的很多復(fù)雜內(nèi)容給封裝簡(jiǎn)化了,當(dāng)然很多動(dòng)態(tài)語(yǔ)言也都再這么干(如PHP),只不過(guò)Python的用戶體驗(yàn)大家一致覺(jué)得更好。

這個(gè)筆記系列,想從源碼的角度來(lái)看,Python是如何把底層復(fù)雜內(nèi)容進(jìn)行封裝的。

第一篇主要先講大致框架,再拿int類型做一些展開(kāi)。基于Python2.7的源碼,Python3.0的源碼會(huì)有區(qū)別,這個(gè)要注意。

1 萬(wàn)物皆對(duì)象,對(duì)象也為對(duì)象

先舉個(gè)例子

Def test(variable):

Print type(variable)

Python中variable可以為任何東西,int, dict, list,string,function。

對(duì)小白來(lái)講,寫函數(shù)不用考慮變量類型,學(xué)習(xí)和使用體驗(yàn)是很好的。(當(dāng)然,在大型項(xiàng)目重構(gòu)的時(shí)候,發(fā)現(xiàn)函數(shù)無(wú)法確定變量類型,返回類型,是很蛋疼的事情。所謂,動(dòng)態(tài)一時(shí)爽,全家火葬場(chǎng))

這種操作,C++中叫多態(tài),而多態(tài)必須有一個(gè)共同的父親節(jié)點(diǎn)。同理,Python底層C實(shí)現(xiàn)也是多態(tài),都有一個(gè)共同的父類。

也就是,萬(wàn)物皆對(duì)象,對(duì)象也為對(duì)象。

1.1 背景知識(shí)——C中的多態(tài)實(shí)現(xiàn)方式

typedef struct {

data member_x;

} base;

typedef struct {

struct base;

data member_y;

} derived;

void function_on_base(struct base * a); // here I can pass both pointers to derived and to basevoid function_on_derived(struct derived * b); // here I must pass a pointer to the derived class

實(shí)現(xiàn)思路其實(shí)很簡(jiǎn)單,base class必須要是一個(gè)struct,繼承類必須要在一開(kāi)始就包含base struct。

1.2 對(duì)象三要素

對(duì)象三要素,引用計(jì)數(shù),類型信息,類型內(nèi)容。

這里從先從父親節(jié)點(diǎn)說(shuō)起,PyObject定義如下

[object.h]

/* Nothing is actually declared to be a PyObject, but every pointer to* a Python object can be cast to a PyObject*. This is inheritance built* by hand. Similarly every pointer to a variable-size Python object can,* in addition, be cast to PyVarObject*.*/

typedef struct _object {

PyObject_HEAD

} PyObject;

#define PyObject_HEAD \_PyObject_HEAD_EXTRA \Py_ssize_t ob_refcnt; \struct _typeobject *ob_type;

注釋已經(jīng)說(shuō)得很清楚,Nothing is actually declared to be a PyObject, but every pointer to a Python object can be cast to a PyObject*. This is inheritance built。

而根據(jù)上文多態(tài)的定義,子類在一開(kāi)始包含PyObject_HEAD即可繼承PyObject對(duì)象。

1.2.1 引用計(jì)數(shù)——Py_ssize_t ob_refcnt

內(nèi)存回收機(jī)制中的核心變量,引用計(jì)數(shù),細(xì)節(jié)不展開(kāi)。

1.2.2 類型對(duì)象——struct _typeobject *ob_type

Python中萬(wàn)物皆對(duì)象有多徹底呢?用來(lái)指定一個(gè)對(duì)象類型的類型變量也是一個(gè)對(duì)象。

[object.h]

typedef int (*printfunc)(PyObject *, FILE *, int);

typedef struct _typeobject {

PyObject_VAR_HEAD //根據(jù)宏定義,PyObject_VAR_HEAD即為PyObject const char *tp_name; /* For printing, in format "." */

printfunc tp_print;

//還有幾十個(gè)函數(shù)指針,省略} PyTypeObject;

typedef struct {

PyObject_VAR_HEAD

} PyVarObject;

#define PyObject_VAR_HEAD \PyObject_HEAD \Py_ssize_t ob_size;/* Number of items in variable part */#define Py_INVALID_SIZE (Py_ssize_t)-1

PyTypeObject就是類型對(duì)象,繼承了PyObject。

這個(gè)對(duì)象通過(guò)大量的函數(shù)指針和多態(tài)來(lái)定義了python對(duì)象所應(yīng)該具有的內(nèi)容。

1.2.3 類型內(nèi)容

PyObject做為父類肯定沒(méi)有類型內(nèi)容,但子類,例如int子類,int內(nèi)容放那呢?

[intobject.h]

typedef struct {

PyObject_HEAD

long ob_ival;

} PyIntObject;

很明顯,在PyObject_HEAD后,加上了long變量來(lái)存儲(chǔ)整數(shù)內(nèi)容。

同理,list,dict,string也是如此設(shè)計(jì),當(dāng)然變長(zhǎng)對(duì)象的設(shè)計(jì)會(huì)更復(fù)雜。

1.3 Python對(duì)象的多態(tài)

類型對(duì)象PyTypeObject通過(guò)函數(shù)指針加多態(tài)來(lái)實(shí)現(xiàn),這里拿printfunc來(lái)舉例。

[object.h]

typedef int (*printfunc)(PyObject *, FILE *, int);

typedef struct _typeobject {

PyObject_VAR_HEAD

const char *tp_name; /* For printing, in format "." */

printfunc tp_print; //這里定義接口 //還有幾十個(gè)函數(shù)指針,省略} PyTypeObject;

[intobject.c]

PyTypeObject PyInt_Type = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"int",

(printfunc)int_print, /* tp_print */

int_print(PyIntObject *v, FILE *fp, int flags) //在這里實(shí)現(xiàn)接口 /* flags -- not used but required by interface */

{

long int_val = v->ob_ival;

Py_BEGIN_ALLOW_THREADS

fprintf(fp, "%ld", int_val);

Py_END_ALLOW_THREADS

return 0;

}

PyTypeObject 定義printfunc的接口,因?yàn)镻yIntObject是PyObject的子類,所以可以在intobject中實(shí)現(xiàn)這個(gè)接口。換成string,dict,set等對(duì)象實(shí)現(xiàn)原理也一樣。

通過(guò)這三要素,PyObject已經(jīng)把對(duì)象框架搭完畢。如果我們要實(shí)現(xiàn)一個(gè)int對(duì)象,根據(jù)PyObject中的類型中定義的接口,選擇我們所需來(lái)實(shí)現(xiàn)即可。

2 int型對(duì)象分析

int對(duì)象的接口實(shí)現(xiàn)想對(duì)簡(jiǎn)單,但也是有不少有意思的點(diǎn)。

2.1 int對(duì)類型對(duì)象的接口實(shí)現(xiàn)

[intobject.c]

PyTypeObject PyInt_Type = {

PyVarObject_HEAD_INIT(&PyType_Type, 0)

"int",

sizeof(PyIntObject),

0,

(destructor)int_dealloc, /* tp_dealloc */

(printfunc)int_print, /* tp_print */

0, /* tp_getattr */

0, /* tp_setattr */

(cmpfunc)int_compare, /* tp_compare */

(reprfunc)int_to_decimal_string, /* tp_repr */

&int_as_number, /* tp_as_number */

0, /* tp_as_sequence */

0, /* tp_as_mapping */

(hashfunc)int_hash, /* tp_hash */

0, /* tp_call */

(reprfunc)int_to_decimal_string, /* tp_str */

PyObject_GenericGetAttr, /* tp_getattro */

0, /* tp_setattro */

0, /* tp_as_buffer */

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |

Py_TPFLAGS_BASETYPE | Py_TPFLAGS_INT_SUBCLASS, /* tp_flags */

int_doc, /* tp_doc */

0, /* tp_traverse */

0, /* tp_clear */

0, /* tp_richcompare */

0, /* tp_weaklistoffset */

0, /* tp_iter */

0, /* tp_iternext */

int_methods, /* tp_methods */

0, /* tp_members */

int_getset, /* tp_getset */

0, /* tp_base */

0, /* tp_dict */

0, /* tp_descr_get */

0, /* tp_descr_set */

0, /* tp_dictoffset */

0, /* tp_init */

0, /* tp_alloc */

int_new, /* tp_new */

};

可以看到,并不是類型對(duì)象所有定義的接口,int對(duì)象都需要實(shí)現(xiàn),賦值為0即代表不用實(shí)現(xiàn)。

上文已拿int_print講過(guò)了,更多代碼細(xì)節(jié)建議去看源碼。

2.2 整數(shù)內(nèi)存池

對(duì)C來(lái)講,棧的內(nèi)存申請(qǐng)和銷毀速度要比堆快的多,為什么就不展開(kāi)了。

C中的int,bool等build-in變量都是在棧上操作。Python中萬(wàn)物皆對(duì)象,也就是struct,新建的int對(duì)象要通過(guò)malloc在堆上申請(qǐng)。

這樣速度必然要比C慢一大截,并且日常代碼中,整數(shù)類型的使用是非常頻繁的。

所以,Python就引入內(nèi)存池和內(nèi)存塊來(lái)進(jìn)行加速。

2.2.1 小整數(shù)對(duì)象內(nèi)存池

PyIntObject是不可變對(duì)象,所以可以提前申請(qǐng)內(nèi)存池來(lái)存儲(chǔ)常用的小數(shù)字,直接從內(nèi)存池來(lái)拿就可以使用。

問(wèn)題是,多小的整數(shù)算小整數(shù)呢?Python是可以自定義的。

[intobject.c]

/* References to small integers are saved in this array so that theycan be shared.The integers that are saved are those in the range-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).*/

static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

代碼如上,注釋也說(shuō)的比較清晰。

2.2.2 大整數(shù)對(duì)象內(nèi)存塊

小整數(shù)對(duì)象通過(guò)固定的內(nèi)存池解決了內(nèi)存重復(fù)申請(qǐng)的問(wèn)題。大整數(shù)對(duì)象是Python申請(qǐng)了一塊固定的內(nèi)存塊,這些內(nèi)存塊由大整數(shù)輪流使用。

核心是兩個(gè)鏈表指針,分成四步走

[intobject.c]

struct _intblock {

struct _intblock *next;

PyIntObject objects[N_INTOBJECTS];

};

typedef struct _intblock PyIntBlock;

static PyIntBlock *block_list = NULL;

static PyIntObject *free_list = NULL;

第一步,整數(shù)如果為小整數(shù),則直接從小整數(shù)內(nèi)存池中取。

第二步,free_list如果不為null,則把free_list指向的空余內(nèi)存分配給當(dāng)前大數(shù)。

第三步,free_list如果為null,則申請(qǐng)一個(gè)PyIntBlock對(duì)象,一個(gè)PyIntBlock可以存多少個(gè)int對(duì)抗,量級(jí)可以自定義。

第四步,新申請(qǐng)的內(nèi)存空間,用free_list串起來(lái)即可。具體參看intobject.c中的fill_free_list函數(shù)

還有兩個(gè)關(guān)鍵步驟。

第一,Python是引用計(jì)數(shù)來(lái)釋放內(nèi)存,int類型內(nèi)存釋放后,free_list也要繼續(xù)把這些free的內(nèi)存串聯(lián)起來(lái)。

第二,假如某個(gè)階段int類型申請(qǐng)?zhí)貏e多,PyIntBlock自然也就申請(qǐng)了很多。然后某個(gè)階段int被集中銷毀,那么多個(gè)PyIntBlock是否完全保留,全都用free_list串起來(lái)?還是銷毀大部分,只保留小部分?這塊代碼沒(méi)細(xì)看。

這樣的好處?

核心就一個(gè),減少堆的碎片化。碎片化的壞處這里就不展開(kāi),Java中專門針對(duì)這個(gè)問(wèn)題其實(shí)做了不少優(yōu)化。

舉個(gè)例子,C++的hash有一個(gè)內(nèi)存碎片的問(wèn)題,因?yàn)槊總€(gè)hash值指向的list都是用鏈表,鏈表的內(nèi)存是分散的。對(duì)于超大的hash存儲(chǔ)來(lái)講,會(huì)導(dǎo)致堆的碎片化問(wèn)題。

有一個(gè)優(yōu)化的方式,就是讓hash值指向的是個(gè)偽鏈表,實(shí)際上是個(gè)連續(xù)型內(nèi)存。這個(gè)操作是不是看著跟上文的介紹有一點(diǎn)類似?

3 絮絮叨叨

這篇文章是基于三年前讀《Python源碼剖析》記的筆記,但一直沒(méi)有完整的整理出來(lái)。

最近換工作,有了空閑時(shí)間,就花了幾天時(shí)間整理了一下,還是挺有意思的。

畢竟身為策略工程師,天天用Python還是挺多的,對(duì)底層有一定了解還是挺好的。

后續(xù)的筆記自然就是繼續(xù)把string,list,dict,再到虛擬機(jī)給寫寫,但抽空吧,可能又是三年后了呢。

總結(jié)

以上是生活随笔為你收集整理的python底层源码_python源码剖析——系列一的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。