Python源碼剖析筆記7-類機(jī)制

拖了好一段時間了氏淑,終于有空來看看python中的類機(jī)制了啄寡。內(nèi)容太多档押,感覺有些地方還是模糊的仰猖,先寫一些吧套啤,有錯誤煩請指出帝牡。

1 Python對象模型

1.1 概述

python2.2之前的這里就不考慮了婴洼,從2.2之后python對象分為兩類海蔽,class對象和instance對象,另外還有個術(shù)語type用來表示“類型”钥组,當(dāng)然class有時候也表示類型這個概念输硝,比如下面的代碼,我們定義了一個名為A的class對象程梦,它的類型是type点把。并且定義了一個實(shí)例對象a,它的類型是A屿附。

class A(object):
    pass
a = A()

#測試代碼
In [7]: a.__class__
Out[7]: __main__.A

In [8]: type(a)
Out[8]: __main__.A

In [9]: A.__class__
Out[9]: type

In [10]: object.__class__
Out[10]: type

In [12]: A.__bases__
Out[12]: (object,)

In [14]: object.__bases__
Out[14]: ()

In [15]: a.__bases__
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-15-d614806ca736> in <module>()
----> 1 a.__bases__

AttributeError: 'A' object has no attribute '__bases__'

In [16]: isinstance(a, A)
Out[16]: True

In [17]: isinstance(A, object)
Out[17]: True

In [18]: issubclass(A, object)
Out[18]: True

1.2 Python對象之間關(guān)系

如1.1中看到的郎逃,我這里將 <type 'type'>這個特殊的class對象單獨(dú)列出來,因?yàn)樗芴貏e挺份,是所有class對象的類型褒翰,這里我們稱之為metaclass。而<type 'object'>則是所有對象的基類。它們兩者之間還有聯(lián)系优训,我們按照is-kind-ofis-instance-of來劃分關(guān)系朵你,所有class對象的type都是metaclass對象,即在Python的C實(shí)現(xiàn)中對應(yīng)PyType_Type揣非,即所有class對象都是<type 'type'>的實(shí)例(is-instance-of)抡医。而所有class對象的直接或間接基類都是object,即對應(yīng)Python的C實(shí)現(xiàn)中PyBaseObject_Type(is-kind-of)妆兑,更加具體的關(guān)系參見下圖魂拦。

對象之間的關(guān)系.png

2 class對象和instance對象

2.1 slot和descriptor

Python中的class對象都是PyTypeObject結(jié)構(gòu)體類型變量,比如type對應(yīng)在C實(shí)現(xiàn)中是PyType_Type搁嗓,int對應(yīng)則是PyInt_Type。int的類型是type箱靴,但是比較特殊的type腺逛,它的類型是自己,如下所示衡怀。當(dāng)然它們的基類都是object棍矛。

In [2]: type.__class__
Out[2]: type

In [3]: int.__class__
Out[3]: type

In [4]: int.__base__
Out[4]: object

In [5]: type.__base__
Out[5]: object

Python在初始化class對象時會填充tp_dict,這個tp_dict會用來搜索類的方法和屬性等抛杨。Python會對class對象的一些特殊方法進(jìn)行特殊處理够委,這就引出了slot和descriptor的概念,其中對于一些特殊方法比如__repr__怖现,python中會設(shè)置一個對應(yīng)的slot茁帽,由于slot本身不是PyObject類型的,所以呢會增加一個封裝屈嗤,也就是descriptor了潘拨,最終在一個class對象的tp_dict中,方法名如__repr__會指向一個descriptor對象饶号,而descriptor對象是對slot的封裝铁追,slot中會有一個slot function,比如對應(yīng)__repr__的就是slot_tp_repr方法茫船,__init__指向的是slot_tp_init方法琅束。這樣,如果在一個class中重新定義了__repr__方法算谈,則在創(chuàng)建class對象的時候涩禀,就會將默認(rèn)的tp_repr指向的方法替換為該slot_to_repr方法,最終在執(zhí)行tp_repr時濒生,其實(shí)就是執(zhí)行的slot_to_repr方法埋泵,而在slot_to_repr方法中就會搜索并找到該class對象中定義的__repr__方法并調(diào)用,這樣就完成了方法的復(fù)寫。

比如下面的代碼中class A繼承自list丽声,如果沒有復(fù)寫__repr__,則在輸出的時候會調(diào)用list_repr方法礁蔗,打印的是'[]',如果如下面這樣復(fù)寫了,則打印的是'Python'雁社。

>> class A(list):
    def __repr__(self):
        return 'Python'
>> s = '%s' % A()
>> s
   'Python'

2.2 MRO簡析

MRO是指python中的屬性解析順序浴井,因?yàn)镻ython不像Java,Python支持多繼承霉撵,所以需要設(shè)置解析屬性的順序磺浙。MRO搜索規(guī)則如下:

  • 1)先從當(dāng)前class出發(fā),比如下面就是先獲取D徒坡,發(fā)現(xiàn)D的mro列表tp_mro沒有D撕氧,則放入D。
  • 2)獲得C喇完,D的mro列表沒有C伦泥,則加入C。此時锦溪,Python虛擬機(jī)發(fā)現(xiàn)C中存在mro列表不脯,于是轉(zhuǎn)而訪問C的mro列表:
    • 2.1)獲得A,D的列mro表沒有A刻诊,則加入A防楷。
    • 2.2)獲得list,盡管D的mro列表沒有l(wèi)ist则涯,但是后面B的mro列表里面有l(wèi)ist复局,于是這里不把list放到D的mro列表,推遲到處理B時放入是整。
    • 2.3)獲得object肖揣,同理也推遲再放。
  • 3)獲得B浮入,D的mro列表沒有B龙优,則放入B。轉(zhuǎn)而訪問B的mro列表:
    • 3.1)獲得list事秀,將list放入D的mro列表彤断。
    • 3.2)獲得object,將object放入D的mro列表易迹。
  • 4)最終宰衙,D的mro列表為(D,C,A,B,list,object)《糜可以打印D.__mro__查看供炼。所以最終輸出為A:show.
class A(list):
  def show(self):
    print 'A:show'

class B(list):
  def show(self):
    print 'B:show'

class C(A):
  pass

class D(C, B):
  pass


d = D()
d.show()

2.3 class對象和instance對象的__dict__

觀察class對象和instance對象的dict一屋,如下代碼可以看到結(jié)果,class對象的dict對應(yīng)的類的屬性袋哼,而instance對象的dict則是存儲的實(shí)例變量冀墨。

class A(object):
  a = 1
  b = 2

  def __init__(self):
    self.c = 3
    self.d = 4

  def test(self):
    pass

  def __repr__(self):
    return 'A'

a = A()
print A.__dict__
print a.__dict__
print a

##輸出結(jié)果
{'a': 1, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', 'b': 2, '__repr__': <function __repr__ at 0x103eb1e60>, 'test': <function test at 0x103eb1758>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, '__init__': <function __init__ at 0x103eb1140>}
{'c': 3, 'd': 4}
A

2.4 成員函數(shù)

調(diào)用成員函數(shù)時,其實(shí)原理與前一篇分析的函數(shù)原理基本一致涛贯,只是在類中對PyFunctionObject包裝了一層诽嘉,封裝成了PyMethodObject對象,這個對象除了PyFunctionObject對象本身弟翘,還新增了class對象和成員函數(shù)調(diào)用的self參數(shù)虫腋。PyFunctionObject和一個instance對象通過PyMethodObject對象結(jié)合在一起的過程就成為成員函數(shù)的綁定。成員函數(shù)調(diào)用時與一般函數(shù)調(diào)用機(jī)制類似稀余,a.f()函數(shù)調(diào)用實(shí)質(zhì)就是帶了一個位置參數(shù)(instance對象a)的一般函數(shù)調(diào)用悦冀。

class A(object):
  def f(self):
    pass

a = A()
print A.f # <unbound method A.f>
print a.f # <bound method A.f of <__main__.A object at 0x10d8616d0>>

3 Python屬性選擇算法

再談到屬性選擇算法之前,需要再說明下descriptor睛琳。descriptor分為兩種雏门,如下:

  • data descriptor: type中定義了getset的descriptor。
  • no data descriptor: type中只定義了get的descriptor掸掏。

Python屬性選擇算法大致規(guī)則如下:

  • Python虛擬機(jī)按照instance屬性和class屬性順序選擇屬性,instance屬性優(yōu)先級高宙帝。
  • 如果在class屬性中發(fā)現(xiàn)同名的data descriptor丧凤,則data descriptor優(yōu)先級高于instance屬性。
#1.data descriptor優(yōu)先級高于instance屬性
class A(list):
  def __get__(self, obj, cls):
    return 'A __get__'

  def __set__(self, obj, value):
    print 'A __set__'
    self.append(value)

class B(object):
  value = A()

b = B()
b.value = 1
print b.value # A.__get__
print b.__class__.__dict__['value'] # [1]
print b.__dict__['value'] # 報錯

#2.instance屬性優(yōu)先級高于no data descriptor
class A(list):
  def __get__(self, obj, cls):
    return 'A __get__'

class B(object):
  value = A()

b = B()
b.value = 1
print b.value # 1
print b.__class__.__dict__['value'] # []
print b.__dict__['value'] # 1

4 其他

Python對象原理還有些不甚明了的地方步脓,暫時記錄到這里愿待,后續(xù)再補(bǔ)充了。筆記來自《python源碼剖析》一書的12章靴患。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仍侥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鸳君,更是在濱河造成了極大的恐慌农渊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件或颊,死亡現(xiàn)場離奇詭異砸紊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)囱挑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門醉顽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人平挑,你說我怎么就攤上這事游添∠挡荩” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵唆涝,是天一觀的道長找都。 經(jīng)常有香客問我,道長石抡,這世上最難降的妖魔是什么檐嚣? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮啰扛,結(jié)果婚禮上嚎京,老公的妹妹穿的比我還像新娘。我一直安慰自己隐解,他們只是感情好鞍帝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煞茫,像睡著了一般帕涌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上续徽,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天蚓曼,我揣著相機(jī)與錄音,去河邊找鬼钦扭。 笑死纫版,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的客情。 我是一名探鬼主播其弊,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼膀斋!你這毒婦竟也來了梭伐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤仰担,失蹤者是張志新(化名)和其女友劉穎糊识,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惰匙,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡技掏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了项鬼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哑梳。...
    茶點(diǎn)故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绘盟,靈堂內(nèi)的尸體忽然破棺而出鸠真,到底是詐尸還是另有隱情悯仙,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布吠卷,位于F島的核電站锡垄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祭隔。R本人自食惡果不足惜货岭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疾渴。 院中可真熱鬧千贯,春花似錦、人聲如沸搞坝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桩撮。三九已至敦第,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間店量,已是汗流浹背芜果。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留融师,地道東北人师幕。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像诬滩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子灭将,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理疼鸟,服務(wù)發(fā)現(xiàn),斷路器庙曙,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉空镜,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評論 0 9
  • # 第一優(yōu)先級規(guī)則聲明: # 除了夢境,每一個意識主進(jìn)程都必須與一個身體參與的機(jī)械進(jìn)程相匹配捌朴,否則結(jié)束意識主進(jìn)程吴攒。...
    李洞BarryLi閱讀 3,853評論 0 1
  • —01— 從小洼怔,我就是個古怪的孩子,總能提前知道別人不知道的很多事情左驾。 上小學(xué)的時候镣隶,我就跟別人的煩惱不一樣极谊。身邊...
    漁晞閱讀 943評論 15 13
  • ?往下看之前,我們先說兩個概念: 1安岂、皮質(zhì)醇:當(dāng)你緊張時轻猖,大腦會釋放皮質(zhì)醇。而皮質(zhì)醇是有毒的域那,它會使人的思維不清晰...
    朗讀者晟煥閱讀 967評論 0 2