偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

通過 Type 和 Object 之間的關(guān)聯(lián),進(jìn)一步分析類型對(duì)象

開發(fā) 前端
我們看到所有類型對(duì)象的類型都被設(shè)置成了 &PyType_Type,也就是 Python 里的 type。所以結(jié)論很清晰了,雖然內(nèi)置的類型對(duì)象可以看做是 type 的實(shí)例對(duì)象,但它卻不是由 type 實(shí)例化得到的,而是在底層預(yù)定義好,并以全局變量的形式靜態(tài)出現(xiàn)。

楔子

type 和 object 兩者的關(guān)系估計(jì)會(huì)讓很多人感到困惑,我們說(shuō) type 站在類型金字塔的頂端,任何對(duì)象按照類型追根溯源,最終得到的都是 type。而 object 站在繼承金字塔的頂端,任何類型對(duì)象按照繼承關(guān)系追根溯源,最終得到的都是 object。

因此我們可以得出以下結(jié)論:

  • type 的父類是 object
  • object 的類型是 type

驗(yàn)證一下:

print(type.__base__)  # <class 'object'>
print(object.__class__)  # <class 'type'>

打印結(jié)果說(shuō)明結(jié)論正確,但這就奇怪了,type 的父類是 object,而 object 的類型又是 type,那么問題來(lái)了,是先有 type 還是先有 object 呢?帶著這些疑問,開始下面的內(nèi)容。

類是由誰(shuí)創(chuàng)建的

首先必須要澄清一個(gè)事實(shí),類對(duì)象的類型是 type,這句話是沒有問題的。但如果說(shuō)類對(duì)象都是由 type 創(chuàng)建的,就有些爭(zhēng)議了。因?yàn)?nbsp;type 能夠創(chuàng)建的是自定義的類,而內(nèi)置的類在底層是預(yù)先定義好的。

# int、tuple、dict 等內(nèi)置類型
# 在底層是預(yù)先定義好的,以全局變量的形式存在
# 我們直接就可以拿來(lái)用
print(int)  # <class 'int'>
print(tuple)  # <class 'tuple'>

# 但對(duì)于自定義的類,顯然就需要在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建了
# 而創(chuàng)建這一過程,就交給 type 來(lái)做
class Girl:
    pass

然后 type 也只能對(duì)自定義類進(jìn)行屬性上的增刪改,內(nèi)置的類則不行。

class Girl:
    pass

# 給類對(duì)象增加一個(gè)成員函數(shù)
type.__setattr__(
    Girl,
    "info",
    lambda self: "name: 古明地覺, age: 17"
)
# 實(shí)例化之后就可以調(diào)用了
print(Girl().info())  # name: 古明地覺, age: 17

# 但內(nèi)置的類對(duì)象,type 是無(wú)法修改的
try:
    type.__setattr__(int, "a", "b")
except TypeError as e:
    print(e)
"""
TypeError: cannot set 'a' attribute of immutable type 'int'
"""

上一篇文章中我們說(shuō)了,Python 所有的類型對(duì)象(包括 type)都是由 PyTypeObject 結(jié)構(gòu)體實(shí)例化得到的,只不過結(jié)構(gòu)體字段的值不同,得到的類也不同。并且內(nèi)置的類型對(duì)象在底層是預(yù)定義好的,它們?cè)诮忉屍骺磥?lái)是同級(jí)別的,不存在誰(shuí)創(chuàng)建誰(shuí)。

而每一個(gè)對(duì)象都有引用計(jì)數(shù)和類型,然后解釋器將這些類對(duì)象的類型都設(shè)置成了 type,我們舉例說(shuō)明。不過在此之前,需要先說(shuō)一個(gè)宏。

// Include/object.h

// _PyObject_EXTRA_INIT 可以忽略掉
// 然后我們看到這個(gè)宏是用來(lái)初始化引用計(jì)數(shù)和類型的
// 并且引用計(jì)數(shù)的值為 uint32 類型的最大值,因此創(chuàng)建的是永恒對(duì)象
#define PyObject_HEAD_INIT(type)    \
    {                               \
        _PyObject_EXTRA_INIT        \
        { _Py_IMMORTAL_REFCNT },    \
        (type)                      \
    },
    
// 用于初始化引用計(jì)數(shù)、類型和 ob_size
#define PyVarObject_HEAD_INIT(type, size) \
    {                                     \
        PyObject_HEAD_INIT(type)          \
        (size)                            \
    },

下面我們來(lái)看幾個(gè)類型對(duì)象。 

圖片圖片

我們看到所有類型對(duì)象的類型都被設(shè)置成了 &PyType_Type,也就是 Python 里的 type。所以結(jié)論很清晰了,雖然內(nèi)置的類型對(duì)象可以看做是 type 的實(shí)例對(duì)象,但它卻不是由 type 實(shí)例化得到的,而是在底層預(yù)定義好,并以全局變量的形式靜態(tài)出現(xiàn)。

所以內(nèi)置的類型對(duì)象之間不存在誰(shuí)創(chuàng)建誰(shuí),它們都是預(yù)定義好的,只是在定義的時(shí)候,將自身的類型設(shè)置成了 type 而已,包括 type 本身(類型還是 type)。這樣一來(lái),每一個(gè)對(duì)象都會(huì)具有一個(gè)類型,從而將面向?qū)ο罄砟钬瀼氐母訌氐住?/p>

print(int.__class__)
print(tuple.__class__)
print(set.__class__)
print(type.__class__)
"""
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
"""

print(
    type.__class__.__class__.__class__ is type
)  # True

print(
    type(type(type(type(type(type))))) is type
)  # True

好,說(shuō)完了這些之后我們來(lái)正式考察 type 和 object 的底層實(shí)現(xiàn)。

類型對(duì)象的類型:PyType_Type

type 是所有類型對(duì)象的類型,我們稱之為元類型或者元類,即 metaclass,當(dāng)然它同時(shí)也是一個(gè)類型對(duì)象。下面看一下它的底層實(shí)現(xiàn)。

// Objects/typeobject.c

PyTypeObject PyType_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "type",                                     /* tp_name */
    sizeof(PyHeapTypeObject),                   /* tp_basicsize */
    sizeof(PyMemberDef),                        /* tp_itemsize */
    (destructor)type_dealloc,                   /* tp_dealloc */
    offsetof(PyTypeObject, tp_vectorcall),      /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    (reprfunc)type_repr,                        /* tp_repr */
    &type_as_number,                            /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    (ternaryfunc)type_call,                     /* tp_call */
    // ...
};

所有的類型對(duì)象加上元類都是由 PyTypeObject 這個(gè)結(jié)構(gòu)體實(shí)例化得到的,所以它們內(nèi)部的字段都是一樣的。只不過傳入的值不同,實(shí)例化之后得到的結(jié)果也不同,可以是 PyLong_Type、可以是 PyFloat_Type,也可以是這里的 PyType_Type。

再看一下里面的宏 PyVarObject_HEAD_INIT,它用來(lái)初始化引用計(jì)數(shù)、類型和 ob_size,其中類型被初始化成了 &PyType_Type。換句話說(shuō),PyType_Type 里面的 ob_type 字段指向的還是 PyType_Type,而對(duì)應(yīng) Python 的話,就是 type 的類型還是 type。

>>> type.__class__
<class 'type'>
>>> type.__class__.__class__.__class__.__class__.__class__ is type
True
>>> type(type(type(type(type(type))))) is type
True

顯然不管套娃多少次,最終的結(jié)果都是True,這也是符合預(yù)期的。

類型對(duì)象的基類:PyBaseObject_Type

Python 中有兩個(gè)類型對(duì)象比較特殊,一個(gè)是站在類型金字塔頂端的 type,另一個(gè)是站在繼承金字塔頂端的 object??赐炅?type,再來(lái)看看 object。

由于 object 的類型是 type,那么在初始化 PyBaseObject_Type 的時(shí)候,它的 ob_type 一定也被設(shè)置成了 &PyType_Type。

我們看一下 PyBaseObject_Type 的實(shí)現(xiàn),它同樣定義在 Objects/typeobject.c 中。

圖片圖片

類型對(duì)象在創(chuàng)建的時(shí)候,ob_type 字段都會(huì)被初始化成 &PyType_Type,而 object 也不例外,所以它的類型為 type,這個(gè)非常簡(jiǎn)單。但 type 的基類是 object,又是怎么一回事呢?

之前介紹類型對(duì)象的時(shí)候,我們說(shuō)類型對(duì)象內(nèi)部的 tp_base 表示繼承的基類,那么對(duì)于 PyType_Type 來(lái)講,它內(nèi)部的 tp_base 肯定是 &PyBaseObject_Type,即 object。

圖片圖片

但令我們吃鯨的是,它的 tp_base 居然是個(gè) 0,如果為 0 的話則表示沒有這個(gè)屬性,或者說(shuō)基類為空。不是說(shuō) type 的基類是 object 嗎?為啥 tp_base 是 0 呢。

事實(shí)上如果你去看其它類型的話,會(huì)發(fā)現(xiàn)它們內(nèi)部的 tp_base 也是 0。為 0 的原因就在于我們目前看到的類型對(duì)象還不夠完善,因?yàn)?Python 的動(dòng)態(tài)性,顯然不可能在定義的時(shí)候就將所有字段屬性都設(shè)置好、然后解釋器一啟動(dòng)就得到我們平時(shí)使用的類型對(duì)象。

因此目前看到的類型對(duì)象還不是最終形態(tài),有一部分字段屬性是在解釋器啟動(dòng)之后再動(dòng)態(tài)完善的,而這個(gè)完善的過程被稱為類型對(duì)象的初始化,它由函數(shù) PyType_Ready 負(fù)責(zé)。

圖片圖片

首先代碼中的 type 只是一個(gè)普通的參數(shù),當(dāng)解釋器發(fā)現(xiàn)一個(gè)類對(duì)象還沒有初始化時(shí),會(huì)將其作為參數(shù)傳遞給 PyType_Ready,進(jìn)行初始化。

初始化過程會(huì)做很多的工作,用于完善類型對(duì)象,而其中一項(xiàng)工作就是設(shè)置基類。如果發(fā)現(xiàn)類型對(duì)象的基類為空,那么就將基類設(shè)置為 object,因?yàn)樵?Python3 里面新式類都要繼承 object。當(dāng)然啦,這個(gè)類不能是 object 本身,object 的基類是 None,因?yàn)槔^承鏈向上要有一個(gè)終點(diǎn)。

當(dāng) PyType_Ready 完成初始化之后,就得到我們平常使用的類型對(duì)象了,最終 PyType_Type 和 PyBaseObject_Type 的關(guān)系如下。

圖片圖片

因此到目前為止,type 和 object 之間的恩怨糾葛算是真相大白了,總結(jié)一下:

1)和自定義類不同,內(nèi)置的類不是由 type 實(shí)例化得到的,它們都是在底層預(yù)先定義好的,不存在誰(shuí)創(chuàng)建誰(shuí)。只是內(nèi)置的類在定義的時(shí)候,它們的類型都被設(shè)置成了 type。這樣不管是內(nèi)置的類,還是自定義類,在調(diào)用時(shí)都會(huì)執(zhí)行 type 的 __call__ 函數(shù),從而讓它們的行為是一致的。

2)雖然內(nèi)置的類在底層預(yù)定義好了,但還有一些瑕疵,因?yàn)橛幸徊糠诌壿嫙o(wú)法以源碼的形式體現(xiàn),只能在解釋器啟動(dòng)的時(shí)候再動(dòng)態(tài)完善。而這個(gè)完善的過程,便包含了基類的填充,會(huì)將基類設(shè)置成 object。

所以 type 和 object 是同時(shí)出現(xiàn)的,它們的存在需要依賴彼此。首先這兩者會(huì)以不完全體的形式定義在源碼中,并且在定義的時(shí)候?qū)?object 的類型設(shè)置成 type;然后當(dāng)解釋器啟動(dòng)的時(shí)候,再經(jīng)過動(dòng)態(tài)完善,進(jìn)化成完全體,而進(jìn)化的過程中會(huì)將 type 的基類設(shè)置成 object。

所以 object 的類型是 type,type 繼承 object 就是這么來(lái)的。

小結(jié)

至此,我們算是從解釋器的角度完全理清了 Python 中對(duì)象之間的關(guān)系,用之前的一張圖總結(jié)一下。

圖片圖片

當(dāng)然,目前還遠(yuǎn)遠(yuǎn)沒有結(jié)束,后續(xù)還會(huì)針對(duì)內(nèi)置的對(duì)象進(jìn)行專門的剖析,如浮點(diǎn)數(shù)、整數(shù)、字符串、字節(jié)串、元組、列表、字典、集合等等,都會(huì)一點(diǎn)一點(diǎn)剖析。我們會(huì)從 Python 的角度介紹對(duì)象該怎么用,然后再看它的底層實(shí)現(xiàn),最后再用 Python 代碼進(jìn)行驗(yàn)證,加深理解。

責(zé)任編輯:武曉燕 來(lái)源: 古明地覺的編程教室
相關(guān)推薦

2011-07-27 12:58:43

Android MarAndroid應(yīng)用商店

2015-10-19 14:57:51

2019-03-22 10:20:39

加速Windows 10啟動(dòng)

2023-09-01 18:20:43

Chrome代碼測(cè)試版

2020-12-10 20:00:04

數(shù)字貨幣比特幣區(qū)塊鏈

2011-07-29 15:02:22

LifeSize視頻協(xié)作

2021-04-27 11:20:20

機(jī)器學(xué)習(xí)骨科醫(yī)療

2020-06-11 13:01:51

惡意軟件網(wǎng)絡(luò)釣魚網(wǎng)絡(luò)攻擊

2018-09-20 11:04:02

NginxPHP-FPM502

2020-10-10 11:35:10

IBMProject DebAI

2014-01-08 10:22:28

思科Videoscape

2009-11-30 18:35:05

BizSparkDreamSparkWebSiteSpar

2009-08-26 14:48:05

C#委托與事件

2010-03-15 09:40:19

Windows 8研發(fā)

2024-05-10 15:09:34

2009-12-28 10:08:07

OracleSQLDevelope開發(fā)框架

2022-04-18 09:23:38

微軟AndroidWindows 11

2024-10-21 10:45:52

2012-04-30 21:35:08

Windows Pho

2020-09-22 10:49:12

大數(shù)據(jù)旅游技術(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)