欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Python中type與object的恩怨糾葛

 更新時(shí)間:2023年04月12日 09:31:26   作者:古明地覺(jué)的編程教室  
估計(jì)很多人都會(huì)有這樣一個(gè)困惑,object?的類型是?type,但它同時(shí)又是?type?的基類,這是怎么做到的?帶著這個(gè)疑問(wèn),我們開(kāi)始本文的內(nèi)容

在學(xué)習(xí) Python 的時(shí)候,你肯定聽(tīng)過(guò)這么一句話:Python 中一切皆對(duì)象。沒(méi)錯(cuò),在 Python 世界里,一切都是對(duì)象。整數(shù)是一個(gè)對(duì)象、字符串是一個(gè)對(duì)象、字典是一個(gè)對(duì)象,甚至 int, str, list 等等,再加上我們使用 class 關(guān)鍵字自定義的類,它們也是對(duì)象。

像 int, str, list 等基本類型,以及我們自定義的類,由于它們可以表示類型,因此我們稱之為類型對(duì)象;類型對(duì)象實(shí)例化得到的對(duì)象,我們稱之為實(shí)例對(duì)象。但不管是哪種對(duì)象,它們都屬于對(duì)象。

因此 Python 將面向?qū)ο罄砟钬瀼氐姆浅氐?,面向?qū)ο笾械念惡蛯?duì)象在 Python 中都是通過(guò)對(duì)象實(shí)現(xiàn)的。

在面向?qū)ο罄碚撝校嬖谥惡蛯?duì)象兩個(gè)概念,像 int、dict、tuple、以及使用 class 關(guān)鍵字自定義的類型對(duì)象實(shí)現(xiàn)了面向?qū)ο罄碚撝蓄惖母拍?,?123、(1, 2, 3),"xxx" 等等這些實(shí)例對(duì)象則實(shí)現(xiàn)了面向?qū)ο罄碚撝袑?duì)象的概念。但在 Python 里面,面向?qū)ο蟮念惡蛯?duì)象都是通過(guò)對(duì)象實(shí)現(xiàn)的。

我們舉個(gè)例子:

#?dict 是一個(gè)類,因此它屬于類型對(duì)象
#?類型對(duì)象實(shí)例化得到的對(duì)象屬于實(shí)例對(duì)象
print(dict)
"""
<class?'dict'>
"""
print(dict(a=1,?b=2))
"""
{'a':?1,?'b':?2}
"""

因此可以用一張圖來(lái)描述面向?qū)ο笤赑ython中的體現(xiàn):

而如果想查看一個(gè)對(duì)象的類型,可以使用 type,或者通過(guò)對(duì)象的 __class__ 屬性。

numbers?=?[1,?2,?3]
#?查看類型
print(type(numbers))
"""
<class?'list'>
"""
print(numbers.__class__)
"""
<class?'list'>
"""

如果想判斷一個(gè)對(duì)象是不是指定類型的實(shí)例對(duì)象,可以使用 isinstance。

numbers?=?[1,?2,?3]
#?判斷是不是指定類型的實(shí)例對(duì)象
print(isinstance(numbers,?list))
"""
True
"""

但是問(wèn)題來(lái)了,按照面向?qū)ο蟮睦碚搧?lái)說(shuō),對(duì)象是由類實(shí)例化得到的,這在 Python 中也是適用的。既然是對(duì)象,那么就必定有一個(gè)類來(lái)實(shí)例化它,換句話說(shuō)對(duì)象一定要有類型。

至于一個(gè)對(duì)象的類型是什么,就看這個(gè)對(duì)象是被誰(shuí)實(shí)例化的,被誰(shuí)實(shí)例化那么類型就是誰(shuí),比如列表的類型是 list,字典的類型是 dict 等等。

而我們說(shuō) Python 中一切皆對(duì)象,所以像 int, str, tuple 這些內(nèi)置的類對(duì)象也是具有相應(yīng)的類型的,那么它們的類型又是誰(shuí)呢?

我們使用 type 查看一下。

>>>?type(int)
<class?'type'>
>>>?type(str)
<class?'type'>
>>>?type(dict)
<class?'type'>
>>>?type(type)
<class?'type'>

我們看到類型對(duì)象的類型,無(wú)一例外都是 type。而 type 我們也稱其為元類,表示類型對(duì)象的類型。至于 type 本身,它的類型還是 type,所以它連自己都沒(méi)放過(guò),把自己都變成自己的對(duì)象了。

因此在 Python 中,你能看到的任何對(duì)象都是有類型的,我們可以使用 type 查看,也可以獲取該對(duì)象的 __class__ 屬性查看。所以:實(shí)例對(duì)象、類型對(duì)象、元類,Python 中任何一個(gè)對(duì)象都逃不過(guò)這三種身份。

到這里可能有人會(huì)發(fā)現(xiàn)一個(gè)有意思的點(diǎn),我們說(shuō) int 是一個(gè)類對(duì)象,這顯然是沒(méi)有問(wèn)題的。因?yàn)檎驹谡麛?shù)(比如 123)的角度上,int 是一個(gè)不折不扣的類對(duì)象;但如果站在 type 的角度上呢?顯然我們又可以將 int 理解為實(shí)例對(duì)象,因此 class 具有二象性。

至于 type 也是同理,雖然它是元類,但本質(zhì)上也是一個(gè)類對(duì)象。

注:不僅 type 是元類,那些繼承了 type 的類也可以叫做元類。

這些概念上的東西讀起來(lái)可能會(huì)有一點(diǎn)繞,但如果實(shí)際動(dòng)手敲一敲代碼的話,還是很好理解的。

然后 Python 中還有一個(gè)關(guān)鍵的類型(對(duì)象),叫做 object,它是所有類型對(duì)象的基類。不管是什么類,內(nèi)置的類也好,我們自定義的類也罷,它們都繼承自 object。因此 object 是所有類型對(duì)象的基類、或者說(shuō)父類。

那如果我們想獲取一個(gè)類都繼承了哪些基類,該怎么做呢?方式有三種:

class?A:?pass

class?B:?pass

class?C(A):?pass

class?D(B,?C):?pass

#?首先?D?繼承自?B?和?C,?C?又繼承?A
#?我們現(xiàn)在要來(lái)查看?D?繼承的父類

#?方法一:?使用?__base__
print(D.__base__)??
"""
<class?'__main__.B'>
"""

#?方法二:?使用?__bases__
print(D.__bases__)??
"""
(<class?'__main__.B'>,?<class?'__main__.C'>)
"""

#?方法三:?使用?__mro__
print(D.__mro__)
"""
(<class?'__main__.D'>,?<class?'__main__.B'>,?
?<class?'__main__.C'>,?<class?'__main__.A'>,?
?<class?'object'>)
"""
  • __base__:如果繼承了多個(gè)類,那么只顯示繼承的第一個(gè)類,沒(méi)有顯式繼承則返回 <class 'object'>
  • __bases__:返回一個(gè)元組,會(huì)顯示所有直接繼承的父類,沒(méi)有顯式繼承, 則返回 (<class 'object'>,)
  • __mro__: mro(Method Resolution Order)表示方法查找順序,會(huì)從自身出發(fā),找到最頂層的父類。因此返回自身、繼承的基類、以及基類繼承的基類, 一直找到 object

而如果想查看某個(gè)類型是不是另一個(gè)類型的子類,可以通過(guò) issubclass。

print(issubclass(str,?object))
"""
True
"""

因此,到目前為止,關(guān)于 type 和 object,我們可以得出以下兩個(gè)結(jié)論:

  • type站在類型金字塔的最頂端, 任何一個(gè)對(duì)象按照類型追根溯源, 最終得到的都是type;
  • object站在繼承金字塔的最頂端, 任何一個(gè)類型對(duì)象按照繼承關(guān)系追根溯源, 最終得到的都是object;

但要注意的是,我們說(shuō) type 的類型還是 type,但 object 的基類則不再是 object,而是 None。

print(
????type.__class__
)??#?<class?'type'>

#?注:以下打印結(jié)果容易讓人產(chǎn)生誤解
#?它表達(dá)的含義是?object?的基類為空
#?而不是說(shuō)?object?繼承?None
print(
????object.__base__
)??#?None

但為什么 object 的基類是 None,而不是它自身呢?其實(shí)答案很簡(jiǎn)單,Python 在查找屬性或方法的時(shí)候,自身如果沒(méi)有的話,會(huì)按照 __mro__ 指定的順序去基類中查找。所以繼承鏈一定會(huì)有一個(gè)終點(diǎn),否則就會(huì)像沒(méi)有出口的遞歸一樣出現(xiàn)死循環(huán)了。

我們用一張圖將對(duì)象之間的關(guān)系總結(jié)一下:

  • 實(shí)例對(duì)象的類型是類型對(duì)象,類型對(duì)象的類型是元類;
  • 所有類型對(duì)象的基類都收斂于 object;
  • 所有對(duì)象的類型都收斂于 type;

因此 Python 算是將一切皆對(duì)象的理念貫徹到了極致,也正因?yàn)槿绱?,Python 才具有如此優(yōu)秀的動(dòng)態(tài)特性。

但是還沒(méi)結(jié)束,我們?cè)僦匦聦徱曇幌律厦婺菑垐D,會(huì)發(fā)現(xiàn)里面有兩個(gè)箭頭看起來(lái)非常的奇怪。object 的類型是 type,type 又繼承了 object。

>>>?type.__base__
<class?'object'>
>>>?object.__class__
<class?'type'>

因?yàn)?nbsp;type 是所有類的元類,而 object 是所有類的基類,這就說(shuō)明 type 要繼承自 object,而 object 的類型是 type。很多人都會(huì)對(duì)這一點(diǎn)感到奇怪,這難道不是一個(gè)先有雞還是先有蛋的問(wèn)題嗎?其實(shí)不是的,這兩個(gè)對(duì)象是共存的,它們之間的定義其實(shí)是互相依賴的。而具體是怎么一回事,我們一點(diǎn)一點(diǎn)分析。

首先在這里必須要澄清一個(gè)事實(shí),類對(duì)象的類型是 type,這句話是沒(méi)有問(wèn)題的;但如果說(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)建這一過(guò)程,就交給?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:?古明地覺(jué),?age:?17"
)
#?實(shí)例化之后就可以調(diào)用了
print(Girl().info())??#?name:?古明地覺(jué),?age:?17

#?但內(nèi)置的類對(duì)象,type?是無(wú)法修改的
try:
????type.__setattr__(int,?"a",?"b")
except?TypeError?as?e:
????print(e)
"""
can't?set?attributes?of?built-in/extension?type?'int'
"""

而 Python 所有內(nèi)置的類對(duì)象,在解釋器看來(lái),都是同級(jí)別的。因?yàn)樗鼈兌际怯赏粋€(gè)結(jié)構(gòu)體實(shí)例化得到的。

所有內(nèi)置的類對(duì)象都是 PyTypeObject 結(jié)構(gòu)體實(shí)例,只不過(guò)結(jié)構(gòu)體字段的值不同,得到的類也不同。所以元類 type 和普通的類對(duì)象,在解釋器看來(lái)都是等價(jià)的。

在解釋器看來(lái),它們無(wú)一例外都是PyTypeObject結(jié)構(gòu)體實(shí)例。換句話說(shuō),它們都是基于這個(gè)結(jié)構(gòu)體創(chuàng)建出的全局變量罷了,這些變量代表的就是 Python 的類。

而每一個(gè)對(duì)象都有引用計(jì)數(shù)和類型,然后解釋器將這些類對(duì)象的類型都設(shè)置成了 type,我們以 object 為例:

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

至于 type 也是同理:

解釋器只是將 type 的類型設(shè)置成了它自身而已,所以內(nèi)置的類對(duì)象之間不存在誰(shuí)創(chuàng)建誰(shuí)。它們都是預(yù)定義好的,只是在定義的時(shí)候,將自身的類型設(shè)置成 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

現(xiàn)在 object 的類型是 type 我們已經(jīng)搞清楚是怎么一回事了,然后是基類的問(wèn)題。PyTypeObject 結(jié)構(gòu)體內(nèi)部有一個(gè) tp_base,它表示的就是類對(duì)象繼承的基類。

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

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

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

首先代碼中的 type 只是一個(gè)普通的參數(shù),當(dāng)解釋器發(fā)現(xiàn)一個(gè)類對(duì)象還沒(méi)有初始化時(shí),會(huì)將其作為參數(shù)傳遞進(jìn)來(lái),進(jìn)行初始化。base 則顯然是它的基類,然后如果基類為空,并且該類不是 object 的話,那么就將它的基類設(shè)置成 object。所以 Python3 中,所有的類默認(rèn)都繼承 object,當(dāng)然除了 object 本身。

因此到目前為止,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__ 方法,從而讓它們的行為是一致的。

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

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

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

以上就是詳解Python中type與object的恩怨糾葛的詳細(xì)內(nèi)容,更多關(guān)于Python type object的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python FTP批量下載/刪除/上傳實(shí)例

    python FTP批量下載/刪除/上傳實(shí)例

    今天小編就為大家分享一篇python FTP批量下載/刪除/上傳實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-12-12
  • Python多進(jìn)程原理與用法分析

    Python多進(jìn)程原理與用法分析

    這篇文章主要介紹了Python多進(jìn)程原理與用法,結(jié)合實(shí)例形式分析了Python多進(jìn)程原理、開(kāi)啟使用進(jìn)程、進(jìn)程隊(duì)列、進(jìn)程池等相關(guān)概念與使用方法,需要的朋友可以參考下
    2018-08-08
  • python爬蟲(chóng)實(shí)戰(zhàn)之爬取京東商城實(shí)例教程

    python爬蟲(chóng)實(shí)戰(zhàn)之爬取京東商城實(shí)例教程

    這篇文章主要介紹了python爬取京東商城的相關(guān)資料,文中通過(guò)爬取一個(gè)實(shí)例頁(yè)面進(jìn)行了講解,通過(guò)示例代碼和圖文介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-04-04
  • Python+PyQt5實(shí)現(xiàn)數(shù)據(jù)庫(kù)表格動(dòng)態(tài)增刪改

    Python+PyQt5實(shí)現(xiàn)數(shù)據(jù)庫(kù)表格動(dòng)態(tài)增刪改

    這篇文章主要為大家介紹如何利用Python中的PyQt5模塊實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)表格的動(dòng)態(tài)增刪改,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-03-03
  • Python命令行解析模塊詳解

    Python命令行解析模塊詳解

    這篇文章主要介紹了Python命令行解析模塊詳解,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Python對(duì)XML文件實(shí)現(xiàn)增刪改查操作

    Python對(duì)XML文件實(shí)現(xiàn)增刪改查操作

    這篇文章主要為大家詳細(xì)介紹了Python對(duì)XML文件進(jìn)行實(shí)現(xiàn)增刪改查操作的方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的可以了解一下
    2022-11-11
  • Ubuntu 20.04安裝Pycharm2020.2及鎖定到任務(wù)欄的問(wèn)題(小白級(jí)操作)

    Ubuntu 20.04安裝Pycharm2020.2及鎖定到任務(wù)欄的問(wèn)題(小白級(jí)操作)

    這篇文章主要介紹了Ubuntu 20.04安裝Pycharm2020.2及鎖定到任務(wù)欄的問(wèn)題,本教程給大家講解的很詳細(xì),非常適合小白級(jí)操作,需要的朋友可以參考下
    2020-10-10
  • Python文件處理與垃圾回收機(jī)制詳情

    Python文件處理與垃圾回收機(jī)制詳情

    這篇文章主要介紹了Python文件處理與垃圾回收機(jī)制詳情,文件是操作系統(tǒng)提供給用戶應(yīng)用程序操作硬盤的一個(gè)虛擬的概念接口,需要的朋友可以參考下面文章內(nèi)容
    2022-09-09
  • 如何在scrapy中捕獲并處理各種異常

    如何在scrapy中捕獲并處理各種異常

    這篇文章主要介紹了如何在scrapy中捕獲并處理各種異常,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Python實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)采集新型冠狀病毒數(shù)據(jù)實(shí)例

    Python實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)采集新型冠狀病毒數(shù)據(jù)實(shí)例

    在本篇文章里小編給大家整理了關(guān)于Python實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)采集新型冠狀病毒數(shù)據(jù)實(shí)例內(nèi)容,有需要的朋友們可以學(xué)習(xí)參考下。
    2020-02-02

最新評(píng)論