AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁(yè) > 科技  > 軟件

當(dāng)調(diào)用一個(gè) Python 對(duì)象時(shí),背后都經(jīng)歷了哪些過(guò)程?

來(lái)源: 責(zé)編: 時(shí)間:2024-05-22 17:11:36 143觀看
導(dǎo)讀楔子在上一篇文章中,我們分析了對(duì)象是如何創(chuàng)建的,主要有兩種方式,一種是通過(guò)特定類(lèi)型 API,另一種是通過(guò)調(diào)用類(lèi)型對(duì)象。對(duì)于內(nèi)置類(lèi)型的實(shí)例對(duì)象而言,這兩種方式都是支持的,比如列表,我們既可以通過(guò) [ ] 創(chuàng)建,也可以通過(guò) list()

楔子

在上一篇文章中,我們分析了對(duì)象是如何創(chuàng)建的,主要有兩種方式,一種是通過(guò)特定類(lèi)型 API,另一種是通過(guò)調(diào)用類(lèi)型對(duì)象。57c28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于內(nèi)置類(lèi)型的實(shí)例對(duì)象而言,這兩種方式都是支持的,比如列表,我們既可以通過(guò) [ ] 創(chuàng)建,也可以通過(guò) list() 創(chuàng)建,前者是列表的特定類(lèi)型 API,后者是調(diào)用類(lèi)型對(duì)象。57c28資訊網(wǎng)——每日最新資訊28at.com

但對(duì)于自定義類(lèi)的實(shí)例對(duì)象而言,我們只能通過(guò)調(diào)用類(lèi)型對(duì)象的方式來(lái)創(chuàng)建。一個(gè)對(duì)象如果可以被調(diào)用,那么這個(gè)對(duì)象就是 callable,否則就不是 callable。而決定一個(gè)對(duì)象是不是 callable,則取決于它的類(lèi)型對(duì)象。57c28資訊網(wǎng)——每日最新資訊28at.com

  • 從 Python 的角度看,如果對(duì)象是 callable,那么它的類(lèi)型對(duì)象一定實(shí)現(xiàn)了 __call__ 函數(shù);
  • 從解釋器的角度看,如果對(duì)象是 callable,那么它的類(lèi)型對(duì)象的 tp_call 字段一定不為空。

57c28資訊網(wǎng)——每日最新資訊28at.com

從 Python 的角度看對(duì)象的調(diào)用

57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

調(diào)用 int 可以創(chuàng)建一個(gè)整數(shù),調(diào)用 str 可以創(chuàng)建一個(gè)字符串,調(diào)用 tuple 可以創(chuàng)建一個(gè)元組,調(diào)用自定義的類(lèi)也可以創(chuàng)建出相應(yīng)的實(shí)例對(duì)象,這就說(shuō)明類(lèi)型對(duì)象是可調(diào)用的,也就是 callable。57c28資訊網(wǎng)——每日最新資訊28at.com

既然類(lèi)型對(duì)象可調(diào)用,那么類(lèi)型對(duì)象的類(lèi)型對(duì)象(type)內(nèi)部一定實(shí)現(xiàn)了 __call__ 函數(shù)。57c28資訊網(wǎng)——每日最新資訊28at.com

# int 可以調(diào)用,那么它的類(lèi)型對(duì)象、也就是元類(lèi)(type)# 內(nèi)部一定實(shí)現(xiàn)了 __call__ 函數(shù)print(hasattr(type, "__call__"))  # True# 而調(diào)用一個(gè)對(duì)象,等價(jià)于調(diào)用其類(lèi)型對(duì)象的 __call__ 函數(shù)# 所以 int(2.71) 實(shí)際就等價(jià)于如下print(type.__call__(int, 2.71))  # 2

我們說(shuō) int、str、float 這些都是類(lèi)型對(duì)象(簡(jiǎn)單來(lái)說(shuō)就是類(lèi)),而 123、"你好"、2.71 是其對(duì)應(yīng)的實(shí)例對(duì)象,這些都沒(méi)問(wèn)題。但相對(duì) type 而言,int、str、float 是不是又成了實(shí)例對(duì)象呢?因?yàn)樗鼈兊念?lèi)型是 type。57c28資訊網(wǎng)——每日最新資訊28at.com

所以 class 具有二象性:57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

  • 如果站在實(shí)例對(duì)象(如:123、"satori"、2.71)的角度上,它是類(lèi)型對(duì)象;
  • 如果站在 type 的角度上,它是實(shí)例對(duì)象;

同理,由于 type 的類(lèi)型還是 type,那么 type 既是 type 的類(lèi)型對(duì)象,type 也是 type 的實(shí)例對(duì)象。雖然這里描述的有一些繞,但應(yīng)該不難理解,而為了避免后續(xù)的描述出現(xiàn)歧義,這里我們做一個(gè)申明:57c28資訊網(wǎng)——每日最新資訊28at.com

  • 整數(shù)、浮點(diǎn)數(shù)、字符串、列表等等,我們稱之為實(shí)例對(duì)象
  • int、float、str、dict,以及自定義的類(lèi),我們稱之為類(lèi)型對(duì)象
  • type 雖然也是類(lèi)型對(duì)象,但我們稱它為元類(lèi)

由于 type 的內(nèi)部定義了 __call__ 函數(shù),那么說(shuō)明類(lèi)型對(duì)象都是可調(diào)用的,因?yàn)檎{(diào)用類(lèi)型對(duì)象就是調(diào)用元類(lèi) type 的 __call__ 函數(shù)。而實(shí)例對(duì)象能否調(diào)用就不一定了,這取決于它的類(lèi)型對(duì)象是否定義了 __call__ 函數(shù),因?yàn)檎{(diào)用一個(gè)對(duì)象,本質(zhì)上是調(diào)用其類(lèi)型對(duì)象內(nèi)部的 __call__ 函數(shù)。57c28資訊網(wǎng)——每日最新資訊28at.com

class A:    passa = A()# 因?yàn)樽远x的類(lèi) A 里面沒(méi)有 __call__# 所以 a 是不可以被調(diào)用的try:    a()except Exception as e:    # 告訴我們 A 的實(shí)例對(duì)象不可以被調(diào)用    print(e)  # 'A' object is not callable# 如果我們給 A 設(shè)置了一個(gè) __call__type.__setattr__(A, "__call__", lambda self: "這是__call__")# 發(fā)現(xiàn)可以調(diào)用了print(a())  # 這是__call__

這就是動(dòng)態(tài)語(yǔ)言的特性,即便在類(lèi)創(chuàng)建完畢之后,依舊可以通過(guò) type 進(jìn)行動(dòng)態(tài)設(shè)置,而這在靜態(tài)語(yǔ)言中是不支持的。所以 type 是所有類(lèi)的元類(lèi),它控制了自定義類(lèi)的生成過(guò)程,因此 type 這個(gè)古老而又強(qiáng)大的類(lèi)可以讓我們玩出很多新花樣。57c28資訊網(wǎng)——每日最新資訊28at.com

但對(duì)于內(nèi)置的類(lèi),type 是不可以對(duì)其動(dòng)態(tài)增加、刪除或者修改屬性的,因?yàn)閮?nèi)置的類(lèi)在底層是靜態(tài)定義好的。從源碼中我們看到,這些內(nèi)置的類(lèi)、包括元類(lèi),它們都是 PyTypeObject 對(duì)象,在底層已經(jīng)被聲明為全局變量了,或者說(shuō)它們已經(jīng)作為靜態(tài)類(lèi)存在了。所以 type 雖然是所有類(lèi)型對(duì)象的類(lèi)型,但只有在面對(duì)我們自定義的類(lèi),type 才具有對(duì)屬性進(jìn)行增刪改的能力。57c28資訊網(wǎng)——每日最新資訊28at.com

而且在上一篇文章中我們也解釋過(guò),Python 的動(dòng)態(tài)性是解釋器將字節(jié)碼翻譯成 C 代碼的時(shí)候動(dòng)態(tài)賦予的,因此給類(lèi)對(duì)象動(dòng)態(tài)設(shè)置屬性只適用于動(dòng)態(tài)類(lèi),也就是在 py 文件中使用 class 關(guān)鍵字定義的類(lèi)。57c28資訊網(wǎng)——每日最新資訊28at.com

而對(duì)于靜態(tài)類(lèi),它們?cè)诰幾g之后已經(jīng)是指向 C 一級(jí)的數(shù)據(jù)結(jié)構(gòu)了,不需要再被解釋器解釋了,因此解釋器自然也就無(wú)法在它們身上動(dòng)手腳,畢竟彪悍的人生不需要解釋。57c28資訊網(wǎng)——每日最新資訊28at.com

try:    type.__setattr__(dict, "ping", "pong")except Exception as e:    print(e)     """    cannot set 'ping' attribute of immutable type 'dict'    """try:    type.__setattr__(list, "ping", "pong")except Exception as e:    print(e)     """    cannot set 'ping' attribute of immutable type 'list'    """

同理其實(shí)例對(duì)象亦是如此,靜態(tài)類(lèi)的實(shí)例對(duì)象也不可以動(dòng)態(tài)設(shè)置屬性:57c28資訊網(wǎng)——每日最新資訊28at.com

lst = list()try:    lst.name = "古明地覺(jué)"except Exception as e:    print(e)  # 'list' object has no attribute 'name'

在介紹 PyTypeObject 結(jié)構(gòu)體的時(shí)候我們說(shuō)過(guò),靜態(tài)類(lèi)的實(shí)例對(duì)象可以綁定哪些屬性,已經(jīng)寫(xiě)死在 tp_members 字段里面了。57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

從解釋器的角度看對(duì)象的調(diào)用

57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

以內(nèi)置類(lèi)型 list 為例,我們說(shuō)創(chuàng)建一個(gè)列表,可以通過(guò) [ ] 或者 list() 的方式。前者使用列表的特定類(lèi)型 API 創(chuàng)建,[ ] 會(huì)被直接解析成 C 一級(jí)的數(shù)據(jù)結(jié)構(gòu),也就是 PyListObject 實(shí)例;后者使用類(lèi)型對(duì)象創(chuàng)建,對(duì) list 進(jìn)行調(diào)用,最終也得到指向 C 一級(jí)的數(shù)據(jù)結(jié)構(gòu) PyListObject 實(shí)例。57c28資訊網(wǎng)——每日最新資訊28at.com

第一種方式我們已經(jīng)很熟悉了,就是根據(jù)值來(lái)推斷在底層應(yīng)該對(duì)應(yīng)哪一種數(shù)據(jù)結(jié)構(gòu),然后直接創(chuàng)建即可,因?yàn)榻忉屍鲗?duì)內(nèi)置的數(shù)據(jù)結(jié)構(gòu)了如指掌。我們重點(diǎn)來(lái)看第二種方式,也就是通過(guò)調(diào)用類(lèi)型對(duì)象去創(chuàng)建實(shí)例對(duì)象。57c28資訊網(wǎng)——每日最新資訊28at.com

如果一個(gè)對(duì)象可以被調(diào)用,那么它的類(lèi)型對(duì)象中一定要有 tp_call,更準(zhǔn)確的說(shuō)是 tp_call 字段的值是一個(gè)具體的函數(shù)指針,而不是 0。由于 PyList_Type 是可以調(diào)用的,這就說(shuō)明 PyType_Type 內(nèi)部的 tp_call 是一個(gè)函數(shù)指針,這在 Python 的層面我們已經(jīng)驗(yàn)證過(guò)了,下面再來(lái)通過(guò)源碼看一下。57c28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片57c28資訊網(wǎng)——每日最新資訊28at.com

在創(chuàng)建 PyType_Type 的時(shí)候,PyTypeObject 內(nèi)部的 tp_call 字段被設(shè)置成了 type_call。所以當(dāng)我們調(diào)用 PyList_Type 的時(shí)候,會(huì)執(zhí)行 type_call 函數(shù)。57c28資訊網(wǎng)——每日最新資訊28at.com

因此 list() 在 C 的層面上等價(jià)于:57c28資訊網(wǎng)——每日最新資訊28at.com

(&PyList_Type)->ob_type->tp_call(&PyList_Type, args, kwargs);// 即:(&PyType_Type)->tp_call(&PyList_Type, args, kwargs);// 而在創(chuàng)建 PyType_Type 的時(shí)候,給 tp_call 字段傳遞的是 type_call// 因此最終相當(dāng)于type_call(&PyList_Type, args, kwargs)

如果用 Python 來(lái)演示這一過(guò)程的話:57c28資訊網(wǎng)——每日最新資訊28at.com

# 以 list("abcd") 為例,它等價(jià)于lst1 = list.__class__.__call__(list, "abcd")# 等價(jià)于lst2 = type.__call__(list, "abcd")print(lst1)  # ['a', 'b', 'c', 'd']print(lst2)  # ['a', 'b', 'c', 'd']

這就是 list() 的秘密,相信其它類(lèi)型在實(shí)例化的時(shí)候是怎么做的,你已經(jīng)知道了,做法是相同的。57c28資訊網(wǎng)——每日最新資訊28at.com

# dct = dict([("name", "古明地覺(jué)"), ("age", 17)])dct = dict.__class__.__call__(    dict, [("name", "古明地覺(jué)"), ("age", 17)])print(dct)  # {'name': '古明地覺(jué)', 'age': 17}# buf = bytes("hello world", encoding="utf-8")buf = bytes.__class__.__call__(    bytes, "hello world", encoding="utf-8")print(buf)  # b'hello world'

當(dāng)然,目前還沒(méi)有結(jié)束,我們還需要看一下 type_call 的源碼實(shí)現(xiàn)。57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

type_call 源碼解析

57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

調(diào)用類(lèi)型對(duì)象,本質(zhì)上會(huì)調(diào)用 type.__call__,在底層對(duì)應(yīng) type_call 函數(shù),因?yàn)?PyType_Type 的 tp_call 字段被設(shè)置成了 type_call。當(dāng)然調(diào)用 type 也是如此,因?yàn)?type 的類(lèi)型還是 type。57c28資訊網(wǎng)——每日最新資訊28at.com

那么這個(gè) type_call 都做了哪些事情呢?57c28資訊網(wǎng)——每日最新資訊28at.com

static PyObject *type_call(PyTypeObject *type, PyObject *args, PyObject *kwds){       // 參數(shù) type 表示類(lèi)型對(duì)象或者元類(lèi),假設(shè)調(diào)用的是 list,那么它就是 &PyList_Type    // 參數(shù) args 和 kwds 表示位置參數(shù)和關(guān)鍵字參數(shù),args 是元組,kwds 是字典    // 創(chuàng)建的實(shí)例對(duì)象,當(dāng)然也可能是類(lèi)型對(duì)象,取決于參數(shù) type    PyObject *obj;      // 線程狀態(tài)對(duì)象,后續(xù)介紹線程的時(shí)候會(huì)細(xì)說(shuō)    // 此處的線程狀態(tài)對(duì)象是用來(lái)設(shè)置異常的    PyThreadState *tstate = _PyThreadState_GET();    // 如果參數(shù) type 是 &PyType_Type,也就是 Python 中的元類(lèi)    if (type == &PyType_Type) {        // 那么它只能接收一個(gè)位置參數(shù)(查看對(duì)象類(lèi)型)或三個(gè)位置參數(shù)(動(dòng)態(tài)創(chuàng)建類(lèi))        Py_ssize_t nargs = PyTuple_GET_SIZE(args);  // 獲取位置參數(shù)的個(gè)數(shù)        // 如果位置參數(shù)個(gè)數(shù)為 1,并且沒(méi)有傳遞關(guān)鍵字參數(shù),那么直接返回對(duì)象的類(lèi)型        if (nargs == 1 && (kwds == NULL || !PyDict_GET_SIZE(kwds))) {            // Py_TYPE 負(fù)責(zé)獲取對(duì)象類(lèi)型,因此相當(dāng)于 type(args[0])            obj = (PyObject *) Py_TYPE(PyTuple_GET_ITEM(args, 0));            // 增加引用計(jì)數(shù),返回 obj            return Py_NewRef(obj);        }        // 如果位置參數(shù)的個(gè)數(shù)不等于 1,那么一定等于 3        if (nargs != 3) {            PyErr_SetString(PyExc_TypeError,                            "type() takes 1 or 3 arguments");            return NULL;        }    }    // 接下來(lái)執(zhí)行類(lèi)型對(duì)象(也可能是元類(lèi))的 tp_new,也就是 __new__    // 如果不存在,那么會(huì)報(bào)錯(cuò),而在 Python 中見(jiàn)到的報(bào)錯(cuò)信息就是這里指定的    if (type->tp_new == NULL) {        _PyErr_Format(tstate, PyExc_TypeError,                      "cannot create '%s' instances", type->tp_name);        return NULL;    }    // 執(zhí)行類(lèi)型對(duì)象的 __new__    obj = type->tp_new(type, args, kwds);    // 檢測(cè)調(diào)用是否正常,如果調(diào)用正常,那么 obj 一定指向一個(gè)合法的 PyObject    // 而如果 obj 為 NULL,則表示執(zhí)行出錯(cuò),此時(shí)解釋器會(huì)拋出異常    obj = _Py_CheckFunctionResult(tstate, (PyObject*)type, obj, NULL);    if (obj == NULL)        return NULL;    // __new__ 執(zhí)行完之后該執(zhí)行啥了,顯然是 __init__,但需要先做一個(gè)檢測(cè)    // 如果 __new__ 返回的實(shí)例對(duì)象的類(lèi)型不是當(dāng)前類(lèi)型,那么直接返回,不再執(zhí)行 __init__    // 比如自定義 class A,那么在 __new__ 里面應(yīng)該返回 A 的實(shí)例對(duì)象,但假設(shè)返回個(gè) 123    // 由于返回值的類(lèi)型不是當(dāng)前類(lèi)型,那么不再執(zhí)行初始化函數(shù) __init__    if (!PyObject_TypeCheck(obj, type))        return obj;    // 走到這里說(shuō)明類(lèi)型一致,那么執(zhí)行 __init__,將 obj、args、kwds 一起傳過(guò)去    type = Py_TYPE(obj);    if (type->tp_init != NULL) {        int res = type->tp_init(obj, args, kwds);        if (res < 0) {            assert(_PyErr_Occurred(tstate));            Py_SETREF(obj, NULL);        }        else {            assert(!_PyErr_Occurred(tstate));        }    }    // 返回創(chuàng)建的對(duì)象 obj    return obj;}

所以整個(gè)過(guò)程就三步:57c28資訊網(wǎng)——每日最新資訊28at.com

  • 如果傳遞的是元類(lèi),并且只有一個(gè)參數(shù),那么直接返回對(duì)象的類(lèi)型;
  • 否則先調(diào)用 tp_new 為實(shí)例對(duì)象申請(qǐng)內(nèi)存;
  • 再調(diào)用 tp_init(如果有)進(jìn)行初始化,設(shè)置對(duì)象屬性;

所以這對(duì)應(yīng)了 Python 中的 __new__ 和 __init__,其中 __new__ 負(fù)責(zé)為實(shí)例對(duì)象開(kāi)辟一份內(nèi)存,然后返回指向?qū)ο蟮闹羔?,并且該指針?huì)自動(dòng)傳遞給 __init__ 中的 self。57c28資訊網(wǎng)——每日最新資訊28at.com

class Girl:    def __new__(cls, name, age):        print("__new__ 方法執(zhí)行啦")        # 調(diào)用 object.__new__(cls) 創(chuàng)建 Girl 的實(shí)例對(duì)象        # 然后該對(duì)象的指針會(huì)自動(dòng)傳遞給 __init__ 中的 self        return object.__new__(cls)    def __init__(self, name, age):        print("__init__ 方法執(zhí)行啦")        self.name = name        self.age = ageg = Girl("古明地覺(jué)", 16)print(g.name, g.age)"""__new__ 方法執(zhí)行啦__init__ 方法執(zhí)行啦古明地覺(jué) 16"""

__new__ 里面的參數(shù)要和 __init__ 里面的參數(shù)保持一致,因?yàn)闀?huì)先執(zhí)行 __new__,然后解釋器再將 __new__ 的返回值和傳遞的參數(shù)組合起來(lái)一起傳給 __init__。因此從這個(gè)角度講,設(shè)置屬性完全可以在 __new__ 里面完成。57c28資訊網(wǎng)——每日最新資訊28at.com

class Girl:    def __new__(cls, name, age):        self = object.__new__(cls)        self.name = name        self.age = age        return selfg = Girl("古明地覺(jué)", 16)print(g.name, g.age)"""古明地覺(jué) 16"""

這樣也是沒(méi)問(wèn)題的,不過(guò) __new__ 一般只負(fù)責(zé)創(chuàng)建實(shí)例,設(shè)置屬性應(yīng)該交給 __init__ 來(lái)做,畢竟一個(gè)是構(gòu)造函數(shù)、一個(gè)是初始化函數(shù),各司其職。另外由于 __new__ 里面不負(fù)責(zé)初始化,那么它的參數(shù)除了 cls 之外,一般都會(huì)寫(xiě)成 *args 和 **kwargs。57c28資訊網(wǎng)——每日最新資訊28at.com

然后再回過(guò)頭來(lái)看一下 type_call 中的這兩行代碼:57c28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片57c28資訊網(wǎng)——每日最新資訊28at.com

tp_new 應(yīng)該返回該類(lèi)型對(duì)象的實(shí)例對(duì)象,而且一般情況下我們是不重寫(xiě) __new__ 的,會(huì)默認(rèn)執(zhí)行 object 的 __new__。但如果我們重寫(xiě)了,那么必須要手動(dòng)返回 object.__new__(cls)。可如果我們不返回,或者返回其它的話,會(huì)怎么樣呢?57c28資訊網(wǎng)——每日最新資訊28at.com

class Girl:    def __new__(cls, *args, **kwargs):        print("__new__ 方法執(zhí)行啦")        instance = object.__new__(cls)        # 打印看看 instance 到底是個(gè)啥        print("instance:", instance)        print("type(instance):", type(instance))        # 正確做法是將 instance 返回        # 但是我們不返回,而是返回一個(gè)整數(shù) 123        return 123    def __init__(self, name, age):        print("__init__ 方法執(zhí)行啦")g = Girl()"""__new__ 方法執(zhí)行啦instance: <__main__.Girl object at 0x0000019A2B7270A0>type(instance): <class '__main__.Girl'>"""

這里面有很多可以說(shuō)的點(diǎn),首先就是 __init__ 里面需要兩個(gè)參數(shù),但是我們沒(méi)有傳,卻還不報(bào)錯(cuò)。原因就在于這個(gè) __init__ 壓根就沒(méi)有執(zhí)行,因?yàn)?__new__ 返回的不是 Girl 的實(shí)例對(duì)象。57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

通過(guò)打印 instance,我們知道了 object.__new__(cls) 返回的就是 cls 的實(shí)例對(duì)象,而這里的 cls 就是 Girl 這個(gè)類(lèi)本身。所以我們必須要返回 instance,才會(huì)自動(dòng)執(zhí)行相應(yīng)的 __init__。57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

我們?cè)谕獠縼?lái)打印一下創(chuàng)建的實(shí)例對(duì)象吧,看看結(jié)果:57c28資訊網(wǎng)——每日最新資訊28at.com

class Girl:    def __new__(cls, *args, **kwargs):        return 123    def __init__(self, name, age):        print("__init__ 方法執(zhí)行啦")g = Girl()print(g)"""123"""

我們看到打印的結(jié)果是 123,所以再次總結(jié)一下 tp_new 和 tp_init 之間的區(qū)別,當(dāng)然也對(duì)應(yīng) __new__ 和 __init__ 的區(qū)別:57c28資訊網(wǎng)——每日最新資訊28at.com

  • tp_new:為實(shí)例對(duì)象申請(qǐng)內(nèi)存,底層會(huì)調(diào)用 tp_alloc,至于對(duì)象的大小則記錄在 tp_basicsize 字段中,而在 Python 里面則是調(diào)用 object.__new__(cls),然后返回;
  • tp_init:tp_new 的返回值會(huì)自動(dòng)傳遞給 self,然后為 self 綁定相應(yīng)的屬性,也就是進(jìn)行實(shí)例對(duì)象的初始化;

但如果 tp_new 返回的對(duì)象的類(lèi)型不對(duì),比如 type_call 的第一個(gè)參數(shù)接收的是 &PyList_Type,但 tp_new 返回的卻是 PyTupleObject *,那么此時(shí)就不會(huì)執(zhí)行 tp_init。57c28資訊網(wǎng)——每日最新資訊28at.com

對(duì)應(yīng)上面的 Python 代碼就是,Girl 的 __new__ 應(yīng)該返回 Girl 的實(shí)例對(duì)象(指針)才對(duì),但卻返回了整數(shù),因此類(lèi)型不一致,不會(huì)執(zhí)行 __init__。57c28資訊網(wǎng)——每日最新資訊28at.com

所以都說(shuō)類(lèi)在實(shí)例化的時(shí)候會(huì)先調(diào)用 __new__,再調(diào)用 __init__,相信你應(yīng)該知道原因了,因?yàn)樵谠创a中先調(diào)用 tp_new,再調(diào)用 tp_init。所以源碼層面表現(xiàn)出來(lái)的,和我們?cè)?Python 層面看到的是一樣的。57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

小結(jié)

57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

到此,我們就從 Python 和解釋器兩個(gè)層面解釋了對(duì)象是如何調(diào)用的,更準(zhǔn)確的說(shuō)我們是從解釋器的角度對(duì) Python 層面的知識(shí)進(jìn)行了驗(yàn)證,通過(guò) tp_new 和 tp_init 的關(guān)系,來(lái)了解 __new__ 和 __init__ 的關(guān)系。57c28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)然對(duì)象調(diào)用還不止目前說(shuō)的這么簡(jiǎn)單,更多的細(xì)節(jié)隱藏在了幕后。后續(xù)我們會(huì)循序漸進(jìn),一點(diǎn)點(diǎn)地揭開(kāi)它的面紗,并且在這個(gè)過(guò)程中還會(huì)不斷地學(xué)習(xí)到新的東西。比如說(shuō),實(shí)例對(duì)象在調(diào)用方法的時(shí)候會(huì)自動(dòng)將實(shí)例本身作為參數(shù)傳遞給 self,那么它為什么會(huì)傳遞呢?解釋器在背后又做了什么工作呢?這些在之后的文章中都會(huì)詳細(xì)說(shuō)明。57c28資訊網(wǎng)——每日最新資訊28at.com

57c28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-90034-0.html當(dāng)調(diào)用一個(gè) Python 對(duì)象時(shí),背后都經(jīng)歷了哪些過(guò)程?

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 編程語(yǔ)言 Kotlin 2.0.0 現(xiàn)已發(fā)布,K2 編譯器進(jìn)入穩(wěn)定狀態(tài)

下一篇: Rust 正在全面入侵前端

標(biāo)簽:
  • 熱門(mén)焦點(diǎn)
  • 微信語(yǔ)音大揭秘:為什么禁止轉(zhuǎn)發(fā)?

    大家好,我是你們的小米。今天,我要和大家聊一個(gè)有趣的話題:為什么微信語(yǔ)音不可以轉(zhuǎn)發(fā)?這是一個(gè)我們經(jīng)常在日常使用中遇到的問(wèn)題,也是一個(gè)讓很多人好奇的問(wèn)題。讓我們一起來(lái)揭開(kāi)這
  • 從零到英雄:高并發(fā)與性能優(yōu)化的神奇之旅

    作者 | 波哥審校 | 重樓作為公司的架構(gòu)師或者程序員,你是否曾經(jīng)為公司的系統(tǒng)在面對(duì)高并發(fā)和性能瓶頸時(shí)感到手足無(wú)措或者焦頭爛額呢?筆者在出道那會(huì)為此是吃盡了苦頭的,不過(guò)也得
  • 2023年,我眼中的字節(jié)跳動(dòng)

    此時(shí)此刻(2023年7月),字節(jié)跳動(dòng)從未上市,也從未公布過(guò)任何官方的上市計(jì)劃;但是這并不妨礙它成為中國(guó)最受關(guān)注的互聯(lián)網(wǎng)公司之一。從2016-17年的抖音強(qiáng)勢(shì)崛起,到2018年的&ldquo;頭騰
  • 本地生活這塊肥肉,拼多多也想吃一口

    出品/壹覽商業(yè) 作者/李彥編輯/木魚(yú)拼多多也看上本地生活這塊蛋糕了。近期,拼多多在App首頁(yè)&ldquo;充值中心&rdquo;入口上線了本機(jī)生活界面。壹覽商業(yè)發(fā)現(xiàn),該界面目前主要
  • ESG的面子與里子

    來(lái)源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之三伏大幕拉起,各地高溫預(yù)警不絕,但處于厄爾尼諾大&ldquo;烤&rdquo;之下的除了眾生,還有各大企業(yè)發(fā)布的ESG報(bào)告。ESG是&ldquo;環(huán)境保
  • 三星電子Q2營(yíng)收60萬(wàn)億韓元 存儲(chǔ)業(yè)務(wù)營(yíng)收同比仍下滑超過(guò)50%

    7月27日消息,據(jù)外媒報(bào)道,從三星電子所發(fā)布的財(cái)報(bào)來(lái)看,他們主要利潤(rùn)來(lái)源的存儲(chǔ)芯片業(yè)務(wù)在今年二季度仍不樂(lè)觀,營(yíng)收同比仍在大幅下滑,所在的設(shè)備解決方案
  • 三星Galaxy Z Fold/Flip 5國(guó)行售價(jià)曝光 :最低7499元/12999元起

    據(jù)官方此前宣布,三星將于7月26日也就是明天在韓國(guó)首爾舉辦Unpacked活動(dòng),屆時(shí)將帶來(lái)帶來(lái)包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy
  • 首發(fā)天璣9200+ iQOO Neo8系列發(fā)布首銷(xiāo)售價(jià)2299元起

    2023年5月23日晚,iQOO Neo8系列正式發(fā)布。其中,Neo系列首款Pro之作——iQOO Neo8 Pro強(qiáng)悍登場(chǎng),限時(shí)售價(jià)3099元起;價(jià)位段最強(qiáng)性能手機(jī)iQOO Neo8同期上市
  • SN570 NVMe SSD固態(tài)硬盤(pán) 價(jià)格與性能兼具

    SN570 NVMe SSD固態(tài)硬盤(pán)是西部數(shù)據(jù)發(fā)布的最新一代WD Blue系列的固態(tài)硬盤(pán),不僅閃存技術(shù)更為精進(jìn),性能也得到了進(jìn)一步的躍升。WD Blue SN570 NVMe SSD的包裝外
Top