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

一文帶你重溫一下Python的對象模型

 更新時間:2023年04月12日 09:31:58   作者:古明地覺的編程教室  
在面向?qū)ο蟮睦碚撝?,有兩個核心的概念:類和實例。在?Python?里面,類和實例都是對象,也就是所謂的類對象和實例對象。本文主要來大家重溫一下Python對象模型的相關知識,感興趣的可以了解一下

在面向?qū)ο蟮睦碚撝校袃蓚€核心的概念:類和實例。類可以看成是一個模板,實例就是根據(jù)這個模板創(chuàng)建出來的對象。但在 Python 里面,類和實例都是對象,也就是所謂的類對象(或者類型對象)和實例對象。

為了避免后續(xù)出現(xiàn)歧義,我們這里把對象分為三種:

  • 內(nèi)置類對象:比如 int、str、list、type、object 等等;
  • 自定義類對象:通過 class 關鍵字定義的類,當然我們也會把它和上面的內(nèi)置類對象統(tǒng)稱為類對象(或者類型對象);
  • 實例對象:由類對象(內(nèi)置類對象或自定義類對象)創(chuàng)建的實例;

而對象之間存在以下兩種關系:

  • is-kind-of:對應面向?qū)ο罄碚撝凶宇惡透割愔g的關系;
  • is-instance-of:對應面向?qū)ο罄碚撝袑嵗龑ο蠛皖悓ο笾g的關系;

我們舉例說明:

class?Girl(object):

????def?say(self):
????????return?"古明地覺"

girl?=?Girl()
print(girl.say())??#?古明地覺

這段代碼便包含了上面的三種對象:object(內(nèi)置類對象),Girl(自定義類對象),girl(實例對象)。

顯然 Girl 和 object 之間是 is-kind-of 關系,即 Girl 是 object 的子類。值得一提的是,Python3 里面所有的類(除 object)都是默認繼承自 object,即便我們這里不顯式繼承 object,也會默認繼承的,但為了說明,我們就寫上了。

除了 Girl 是 object 的子類,我們還能看出 girl 和 Girl 之間存在 is-instance-of 關系,即 girl 是 Girl 的實例。當然如果再進一步的話,girl 和 object 之間也存在 is-instance-of 關系,girl 也是 object 的實例。

class?Girl(object):
????pass
????
girl?=?Girl()
print(issubclass(Girl,?object))??#?True?
print(type(girl))??#?<class?'__main__.Girl'>
print(isinstance(girl,?Girl))??#?True
print(isinstance(girl,?object))??#?True

girl 是 Girl 這個類實例化得到的,所以 type(girl) 得到的是類對象 Girl。但 girl 也是 object 的實例對象,因為 Girl 繼承了 object。至于這其中的原理,我們會慢慢介紹。

Python 也提供了一些手段可以探測這些關系,除了上面的 type 之外,還可以使用對象的 __class__ 屬性探測一個對象和其它的哪些對象之間存在 is-instance-of 關系。

而通過對象的 __bases__ 屬性則可以探測一個對象和其它的哪些對象之間存在著 is-kind-of 關系。此外 Python 還提供了兩個函數(shù) issubclass 和 isinstance 來驗證兩個對象之間是否存在著我們期望的關系。

class?Girl(object):
????pass?

girl?=?Girl()
print(girl.__class__)??#?<class?'__main__.Girl'>
print(Girl.__class__)??#?<class?'type'>
# __class__是查看自己的類型是什么,也就是生成自己的類
# 而在介紹 Python 對象的時候,我們就看到了
# 任何一個對象都至少具備兩個東西:?一個是引用計數(shù)、一個是類型
# 所以 __class__ 是所有對象都具備的

#?__base__只顯示直接繼承的第一個類
print(Girl.__base__)??#?<class?'object'>
#?__bases__ 會顯示直接繼承的所有類,以元組的形式
print(Girl.__bases__)??#?(<class?'object'>,)

我們畫一張圖總結(jié)一下:

另外需要注意里面的 type 和 object:

  • type 和 object 存在 is-kind-of 關系,因為 type 是 object 的子類;
  • object 和 type 存在 is-instance-of 關系,因為 object 是 type 的實例對象;

可能有人會好奇為什么會是這樣,而關于這一點,我在 type 與 object 的恩怨糾葛這篇文章講得很詳細了,感興趣可以點擊閱讀一下。

簡單來說就是,type 在底層對應的結(jié)構(gòu)體為 PyType_Type、object 在底層對應的結(jié)構(gòu)體為 PyBaseObject_Type。而在創(chuàng)建 object 的時候,將內(nèi)部的 ob_type 設置成了&PyType_Type;在創(chuàng)建type的時候,將內(nèi)部的 tp_base 設置成了&PyBaseObject_Type。

因此這兩者的定義是彼此依賴的,兩者是同時出現(xiàn)的,我們后面還會看到。

另外 type 的類型就是 type 本身,所以:

  • 實例對象的類型是類型對象,類型對象的類型是元類;
  • 所有類型對象的基類都收斂于 object;
  • 所有對象的類型都收斂于 type;

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

但還沒有結(jié)束,我們看一下類對象 Girl 的行為,首先它支持屬性設置:

class?Girl(object):
????pass

print(hasattr(Girl,?"name"))??#?False
Girl.name?=?"古明地覺"
print(hasattr(Girl,?"name"))??#?True
print(Girl.name)??#?古明地覺

一個類都已經(jīng)定義完了,我們后續(xù)還可以進行屬性添加,這在其它的靜態(tài)語言中是不可能做到的。那么Python是如何做到的呢?我們說能夠?qū)傩赃M行動態(tài)添加,你會想到什么?是不是字典呢?

正如 global 名字空間一樣,我們猜測類應該也有自己的屬性字典,往類里面設置屬性的時候,等價于向字典中添加鍵值對,同理其它操作也與之類似。

class?Girl(object):
????pass

print(Girl.__dict__.get("name",?"不存在"))??#?不存在
Girl.name?=?"古明地覺"
print(Girl.__dict__.get("name"))??#?古明地覺

和操作全局變量是類似的,但是有一點需要注意:我們不能直接通過類的屬性字典來設置屬性。

try:
????Girl.__dict__["name"]?=?"古明地覺"
except?Exception?as?e:
????print(e)??
#?'mappingproxy'?object?does?not?support?item?assignment

雖然叫屬性字典,但其實是 mappingproxy 對象,該對象本質(zhì)上就是對字典進行了一層封裝,在字典的基礎上移除了增刪改操作,也就是只保留了查詢功能。如果我們想給類增加屬性,可以采用直接賦值的方式,或者調(diào)用 setattr 函數(shù)也是可以的。

但在介紹如何篡改虛擬機的時候,我們提到過一個騷操作,可以通過 gc 模塊拿到 mappingproxy 對象里的字典。

import?gc

class?Girl(object):
????pass

gc.get_referents(Girl.__dict__)[0]["name"]?=?"古明地覺"
print(Girl.name)??#?古明地覺

并且這種做法除了適用于自定義類對象,還適用于內(nèi)置類對象。但是工作中不要這么做,知道有這么個操作就行。

除了設置屬性之外,我們還可以設置函數(shù)。

class?Girl(object):
????pass

Girl.info?=?lambda?name:?f"我是{name}"
print(Girl.info("古明地覺"))??#?我是古明地覺

#?如果實例調(diào)用的話,會和我們想象的不太一樣
#?因為實例調(diào)用的話會將函數(shù)包裝成方法
try:
????Girl().info("古明地覺")
except?TypeError?as?e:
????print(e)?
"""
<lambda>()?takes?1?positional?argument?but?2?were?given
"""????

#?實例在調(diào)用的時候會將自身也作為參數(shù)傳進去
#?所以第一個參數(shù)?name?實際上接收的是?Girl?的實例對象
#?只不過第一個參數(shù)按照規(guī)范來講應該叫做self
#?但即便你起別的名字也是無所謂的
print(Girl().info())??
"""
我是<__main__.Girl?object?at?0x000001920BB88760>
"""

所以我們可以有兩種做法:

#?將其包裝成一個靜態(tài)方法
#?這樣類和實例都可以調(diào)用
Girl.info?=?staticmethod(lambda?name:?f"我是{name}")
print(Girl.info("古明地覺"))??#?我是古明地覺
print(Girl().info("古明地覺"))??#?我是古明地覺

#?如果是給實例用的,那么帶上一個?self?參數(shù)即可
Girl.info?=?lambda?self,?name:?f"我是{name}"
print(Girl().info("古明地覺"))??#?我是古明地覺

此外我們還可以通過 type 來動態(tài)地往類里面進行屬性的增加、修改和刪除。

class?Girl(object):

????def?say(self):
????????pass

print(hasattr(Girl,?"say"))??#?True
#?delattr(Girl,?"say")?與之等價
type.__delattr__(Girl,?"say")
print(hasattr(Girl,?"say"))??#?False
#?我們設置一個屬性吧
#?等價于?Girl.name?=?"古明地覺"
setattr(Girl,?"name",?"古明地覺")
print(Girl.name)??#?古明地覺

事實上調(diào)用 getattr、setattr、delattr 等價于調(diào)用其類型對象的__getattr__、__setattr__、__delattr__。

所以,一個對象支持哪些行為,取決于其類型對象定義了哪些操作。并且通過對象的類型對象,可以動態(tài)地給該對象進行屬性的設置。Python 所有類型對象的類型對象都是 type,通過 type 我們便可以控制類的生成過程,即便類已經(jīng)創(chuàng)建完畢了,也依舊可以進行屬性設置。

但是注意:type 可以操作的類只能是通過 class 定義的動態(tài)類,而像 int、list、dict 等靜態(tài)類,它們是在源碼中靜態(tài)定義好的,只不過類型設置成了 type。一言以蔽之,type 雖然是所有類對象的類對象,但 type 只能對動態(tài)類進行屬性上的修改,不能修改靜態(tài)類。

try:
????int.name?=?"古明地覺"
except?Exception?as?e:
????print(e)
"""
can't?set?attributes?of?built-in/extension?type?'int'
"""

try:
????setattr(int,?"ping",?"pong")
except?Exception?as?e:
????print(e)
"""
can't?set?attributes?of?built-in/extension?type?'int'?????
"""

通過報錯信息可以看到,不可以設置內(nèi)置類和擴展類的屬性,因為內(nèi)置類在解釋器啟動之后,就已經(jīng)初始化好了。至于擴展類就是我們使用 Python/C API 編寫的擴展模塊中的類,它和內(nèi)置類是等價的。

因此內(nèi)置類和使用 class 定義的類本質(zhì)上是一樣的,都是 PyTypeObject 對象,它們的類型在 Python 里面都是 type。但區(qū)別在于內(nèi)置類在底層是靜態(tài)初始化的,我們不能進行屬性的動態(tài)設置(通過 gc 模塊實現(xiàn)除外)。

但是為什么不可以對內(nèi)置類和擴展類進行屬性設置呢?首先我們要知道 Python 的動態(tài)特性是虛擬機賜予的,而虛擬機的工作就是將 PyCodeObject 對象翻譯成 C 的代碼進行執(zhí)行,所以 Python 的動態(tài)特性就是在這一步發(fā)生的。

而內(nèi)置類在解釋器啟動之后就已經(jīng)靜態(tài)初始化好了,直接指向 C 一級的數(shù)據(jù)結(jié)構(gòu),同理擴展類也是如此。它們相當于繞過了解釋執(zhí)行這一步,所以它們的屬性不可以動態(tài)添加。

不光內(nèi)置的類本身,還有它的實例對象也是如此。

a?=?123
print(hasattr(a,?"__dict__"))??#?False

我們看到它連自己的屬性字典都沒有,因為內(nèi)置類對象的實例對象,內(nèi)部有哪些屬性,解釋器記得清清楚楚。它們在底層都已經(jīng)寫死了,并且不允許修改,因此虛擬機完全沒有必要為其實現(xiàn)屬性字典(節(jié)省了內(nèi)存占用)。

到此這篇關于一文帶你重溫一下Python的對象模型的文章就介紹到這了,更多相關Python對象模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 用python與文件進行交互的方法

    用python與文件進行交互的方法

    本篇文章主要介紹了用python與文件進行交互的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • Python文件處理

    Python文件處理

    這篇文章主要介紹了Python文件處理的相關資料,需要的朋友可以參考下
    2016-02-02
  • Python中csv模塊的基本使用教程

    Python中csv模塊的基本使用教程

    csv文件使用逗號分割,是一種純文本格式,不能指定字體顏色等樣式,也不能指定單元格的寬高,不能合并單元格,沒有多個工作表等功能,可以使用Excel打,這篇文章主要給大家介紹了關于Python中csv模塊的基本使用教程,需要的朋友可以參考下
    2021-07-07
  • pycharm中使用request和Pytest進行接口測試的方法

    pycharm中使用request和Pytest進行接口測試的方法

    這篇文章主要介紹了pycharm中使用request和Pytest進行接口測試的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • Python實現(xiàn)新浪博客備份的方法

    Python實現(xiàn)新浪博客備份的方法

    這篇文章主要介紹了Python實現(xiàn)新浪博客備份的方法,涉及Python正則操作,字符串操作及文本操作的相關技巧,需要的朋友可以參考下
    2016-04-04
  • Python 詳解爬取并統(tǒng)計CSDN全站熱榜標題關鍵詞詞頻流程

    Python 詳解爬取并統(tǒng)計CSDN全站熱榜標題關鍵詞詞頻流程

    讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Python爬取CSDN全站綜合熱榜標題,順便統(tǒng)計關鍵詞詞頻,大家可以在過程中查缺補漏,提升水平
    2021-11-11
  • python編寫adb截圖工具的實現(xiàn)源碼

    python編寫adb截圖工具的實現(xiàn)源碼

    adb截圖工具可用于Android手機及Android終端,Android端或者Android終端的遠程截圖至本地電腦中,今天通過本文給大家介紹python編寫adb截圖工具的實現(xiàn)源碼,感興趣的朋友一起看看吧
    2021-08-08
  • TensorFlow keras卷積神經(jīng)網(wǎng)絡 添加L2正則化方式

    TensorFlow keras卷積神經(jīng)網(wǎng)絡 添加L2正則化方式

    這篇文章主要介紹了TensorFlow keras卷積神經(jīng)網(wǎng)絡 添加L2正則化方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-05-05
  • 使用Python根據(jù)一個列表的順序?qū)ζ渌斜磉M行排序

    使用Python根據(jù)一個列表的順序?qū)ζ渌斜磉M行排序

    這篇文章主要介紹了使用Python根據(jù)一個列表的順序?qū)ζ渌斜磉M行排序,根據(jù)列表B中每個元素的下標來獲取列表A中對應位置的元素,將其作為排序依據(jù)即可,需要的朋友可以參考下
    2023-10-10
  • Python實現(xiàn)爬蟲設置代理IP和偽裝成瀏覽器的方法分享

    Python實現(xiàn)爬蟲設置代理IP和偽裝成瀏覽器的方法分享

    今天小編就為大家分享一篇Python實現(xiàn)爬蟲設置代理IP和偽裝成瀏覽器的方法分享,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05

最新評論