用一個(gè)全家桶一樣的例子開啟討論!
class superC:
X = 3
X = 1
def nester():
X = 2
print(X)
class C(superC):
X = 3
print(X)
def method1(self):
print(X)
def method2(self):
X = 4
print(X)
return C()
print(X)
I = nester()
I.method1()
I.method2()
print(I.X, end=' ')
>> 1 2 3 2 4 3
下面來逐個(gè)分析一下都办。
在逐個(gè)分析之前嫡锌,對(duì)python的名稱解析做一下說明。python的名稱解析有兩套邏輯琳钉,一套用于變量(函數(shù)的變量势木、模塊中的全局變量),一套用于對(duì)象屬性歌懒。出現(xiàn)在表達(dá)式中的未知名稱會(huì)被視作變量啦桌,obj.attr
語法中的attr
被視作屬性。
兩套邏輯都沿著層層嵌套的名稱空間順藤摸瓜及皂,只是藤不一樣甫男。變量解析沿著“LEGB”順序,即Local, Enclosing, Global, Built-in验烧。屬性解析沿著先實(shí)例再繼承樹的順序板驳,繼承樹按層序遍歷,同層按定義父類時(shí)括號(hào)中從左到右的順序噪窘。
兩套邏輯在代碼層面相互交織笋庄,比如obj.attr
屬性解析attr
之前一定得先解析變量obj,但在邏輯層面不會(huì)混淆倔监。
再說一下python的四種名稱空間直砂,包、模塊浩习、函數(shù)静暂、類。
- 包在文件系統(tǒng)中是一個(gè)或多個(gè)列在
sys.path
下的同名文件夾谱秽,其屬性取自文件夾中的__init__.py文件的Global變量洽蛀。 - 模塊在文件系統(tǒng)中是一個(gè)文件,被其他模塊
import
時(shí)疟赊,其Global變量變成模塊的屬性郊供。 - 函數(shù)有一個(gè)臨時(shí)的本地變量空間,函數(shù)調(diào)用結(jié)束后這個(gè)空間就消失了近哟。但在函數(shù)嵌套時(shí)驮审,被內(nèi)層函數(shù)引用的外層函數(shù)的變量不會(huì)消失。
- 類的本地變量成為類對(duì)象的屬性。但嵌套的類不會(huì)在外層類的本地空間中查找變量疯淫,而是遵守一般的LEGB規(guī)則地来。事實(shí)上,變量解析始終遵守LEGB熙掺,嵌套的類名稱空間根本不起作用未斑。
回到對(duì)開頭的例子,逐個(gè)分析:
- 先輸出
X
1是因?yàn)楹瘮?shù)nester的函數(shù)體不在定義時(shí)執(zhí)行币绩,而在調(diào)用時(shí)執(zhí)行蜡秽,本例中調(diào)用語句在X
1輸出語句的后面。 -
X
2是nester
的本地變量类浪,遮蓋了全局的X
1载城。 - 比較微妙的是
X
3,是類C
的本地變量费就,能被同空間中的print
引用,但不被method1
引用川队。 -
method1
引用的是nester
的X
力细。
有意思的是,method1
的調(diào)用是通過I
的屬性解析實(shí)現(xiàn)的固额,而這件事發(fā)生在nester
的調(diào)用結(jié)束后眠蚂,nester
的本地變量空間已經(jīng)銷毀了。但由于在編譯method1
時(shí)發(fā)現(xiàn)它引用了外層的X
斗躏,這個(gè)被引用的變量就得到了特殊對(duì)待逝慧,沒有和nester
的空間一并銷毀。 -
X
4是method2
的本地變量啄糙,LEGB的頭一個(gè)笛臣。 -
I.X
是本例中唯一的屬性解析。X
不在I
的本地空間中隧饼,我們從未給I
賦此屬性沈堡。X
在C
的本地空間中(C
的本地空間在定義后變?yōu)?code>C類對(duì)象的屬性)。沿著實(shí)例燕雁、繼承樹順序诞丽,I.X
最終在C
類的屬性空間中找到X
。
元類拐格、裝飾器……超出本文范圍僧免。