python类的构造方法名称_搞懂Python的类和对象名称空间
代碼塊的分類
python中分幾種代碼塊類型,它們都有自己的作用域,或者說(shuō)名稱空間:文件或模塊整體是一個(gè)代碼塊,名稱空間為全局范圍
函數(shù)代碼塊,名稱空間為函數(shù)自身范圍,是本地作用域,在全局范圍的內(nèi)層函數(shù)內(nèi)部可嵌套函數(shù),嵌套函數(shù)有更內(nèi)一層的名稱空間
類代碼塊,名稱空間為類自身類中可定義函數(shù),類中的函數(shù)有自己的名稱空間,在類的內(nèi)層
類的實(shí)例對(duì)象有自己的名稱空間,和類的名稱空間獨(dú)立
類可繼承父類,可以鏈接至父類名稱空間
正是這一層層隔離又連接的名稱空間將變量、類、對(duì)象、函數(shù)等等都組織起來(lái),使得它們可以擁有某些屬性,可以進(jìn)行屬性查找。
本文詳細(xì)解釋類和對(duì)象涉及的名稱空間,屬于純理論類的內(nèi)容,有助于理解python面向?qū)ο蟮募?xì)節(jié)。期間會(huì)涉及全局和本地變量作用域的查找規(guī)則,如有不明白之處,可先看文章:Python作用域詳述
一個(gè)概括全文的示例
以下是一個(gè)能在一定程度上概括全文的示例代碼段:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19x?=?11???????????#?全局變量xdef?f():?????????#?全局變量f
print(x)?????#?引用全局變量xdef?g():?????????#?全局變量g
x?=?22???????#?定義本地變量x
print(x)?????#?引用本地變量xclass?supcls():??????#?全局變量supcls
x?=?33???????????#?類變量x
def?m(self):?????#?類變量m,類內(nèi)函數(shù)變量self
x?=?44???????#?類內(nèi)函數(shù)變量x
self.x?=?55??#?對(duì)象變量xclass?cls(supcls):???#?全局變量cls
x?=?supcls.x?????#?引用父類屬性x,并定義cls類屬性x
def?n(self):?????#?類變量n
self.x?=?66??#?對(duì)象變量x
如果能理解上面的每個(gè)x屬于哪個(gè)作用域、哪個(gè)名稱空間,本文內(nèi)容基本上就理解了。
類的名稱空間
下面有一個(gè)類,類中有類屬性x、y,有類方法m和n。1
2
3
4
5
6
7
8
9
10
11
12class?supcls():
x?=?3
y?=?4
def?m(self):
x?=?33
self.x?=?333
self.y?=?444
self.z?=?555
def?n(self):????????return?self.x,?self.y,?self.z
當(dāng)python解釋到supcls代碼塊后,知道這是一個(gè)類,類有自己的名稱空間。所以,當(dāng)知道了這個(gè)類里面有x、y、m、n后,這幾個(gè)屬性都會(huì)放進(jìn)類supcls的名稱空間中。
如下圖:
在上圖中,類的名稱空間中有屬性x、y、m和n,它們都稱為類屬性。需要說(shuō)明的是,在python中,函數(shù)變量m、n和普通變量沒(méi)什么區(qū)別,僅僅只是它保存了指向函數(shù)體的地址,函數(shù)體即上圖中用func m和func n所表示的對(duì)象。
因?yàn)橛忻Q空間,可以直接使用完全限定名稱去訪問(wèn)這個(gè)名稱空間中的內(nèi)容。例如:1
2
3
4print(supcls.x)print(supcls.y)print(supcls.m)print(supcls.n)
輸出結(jié)果:1
2
3
43
4
因?yàn)楹瘮?shù)m和n也是類的屬性,它們也可以直接通過(guò)類名來(lái)訪問(wèn)執(zhí)行。例如,新加入一個(gè)函數(shù),但不用self參數(shù)了,然后執(zhí)行它。1
2
3
4
5
6
7
8class?testcls():
z?=?3
def?a():
x?=?1
print(x)????????#?print(z)?????#?這是錯(cuò)的testcls.a()
不僅如此,方法也是屬性,它實(shí)際上就是函數(shù)(在python 3.0中確實(shí)如此,在以前版本中方法和函數(shù)是有點(diǎn)區(qū)別的)。所以,可以將方法保存下來(lái),之后再去調(diào)用它:1
2
3aa?=?testcls.aaa()print(aa)
但是需要注意,類方法代碼塊中看不見(jiàn)類變量。雖然類和類方法的作用域關(guān)系類似于全局作用域和函數(shù)本地作用域,但并不總是等價(jià)。例如,方法a()中無(wú)法直接訪問(wèn)類變量z。這就像類內(nèi)部看不到全局變量一樣。
上面全都是使用類名.屬性這種完全限定名稱去訪問(wèn)類中的屬性的。如果生成類的對(duì)象,則可以通過(guò)對(duì)象去訪問(wèn)相關(guān)對(duì)象屬性,因?yàn)閷?duì)象有自己的名稱空間,且部分屬性來(lái)源于類。
對(duì)象名稱空間
類就像一個(gè)模板,可以根據(jù)這個(gè)模板大量生成具有自己特性的對(duì)象。在Python中,只需像調(diào)用函數(shù)一樣直接調(diào)用類就可以創(chuàng)建對(duì)象。
例如,下面創(chuàng)建了兩個(gè)cls類的對(duì)象o1和o2,創(chuàng)建類的時(shí)候可以傳遞參數(shù)給類,這個(gè)參數(shù)可以傳遞給類的構(gòu)造函數(shù)__init__()。1
2o1?=?cls()o2?=?cls("some?args")
對(duì)象有自己的名稱空間。因?yàn)閷?duì)象是根據(jù)類來(lái)創(chuàng)建的,類是它們的模板,所以對(duì)象名稱空間中包含所有類屬性,但是對(duì)象名稱空間中這些屬性的值不一定和類名稱空間屬性的值相同。
現(xiàn)在根據(jù)supcls類構(gòu)造兩個(gè)對(duì)象s1和s2:1
2
3
4
5
6
7
8
9
10
11
12
13class?supcls():
x?=?3
y?=?4
def?m(self):
x?=?33
self.x?=?333
self.y?=?444
self.z?=?555
def?n(self):????????return?self.x,?self.y,?self.z
s1?=?supcls()
s2?=?supcls()
那么它們的名稱空間,以及類的名稱空間的關(guān)系如下圖所示:
現(xiàn)在僅僅只是對(duì)象s1、s2連接到了類supcls,對(duì)象s1和s2有自己的名稱空間。但因?yàn)轭恠upcls中沒(méi)有構(gòu)造方法__init__()初始化對(duì)象屬性,所以它們的名稱空間中除了python內(nèi)部設(shè)置的一些"其它"屬性,沒(méi)有任何屬于自己的屬性。
但因?yàn)閟1、s2連接到了supcls類,所以可以進(jìn)行對(duì)象屬性查找,如果對(duì)象中沒(méi)有,將會(huì)向上找到supcls。例如:1
2
3print(s1.x)????#?輸出3,搜索到類名稱空間print(s1.y)????#?輸出4,搜索到類名稱空間#?print(s1.z)??#?這是錯(cuò)的
上面不再是通過(guò)完全限定的名稱去訪問(wèn)類中的屬性,而是通過(guò)對(duì)象屬性查找的方式搜索到了類屬性。但上面訪問(wèn)z屬性將報(bào)錯(cuò),因?yàn)檫€沒(méi)有調(diào)用m方法。
當(dāng)調(diào)用m方法后,將會(huì)通過(guò)self.xxx的方式設(shè)置完全屬于對(duì)象自身的屬性,包括x、y、z。1
2s1.m()s2.m()
現(xiàn)在,它們的名稱空間以及類的名稱空間的關(guān)系如下圖所示:
現(xiàn)在對(duì)象名稱空間中有x、y和z共3個(gè)屬性(不考慮其它python內(nèi)部設(shè)置的屬性),再通過(guò)對(duì)象名去訪問(wèn)對(duì)象屬性,仍然會(huì)查找屬性,但對(duì)于這3個(gè)屬性的搜索不會(huì)進(jìn)一步搜索到類的名稱空間。但如果訪問(wèn)對(duì)象中沒(méi)有的屬性,比如m和n,它們不存在于對(duì)象的名稱空間中,所以會(huì)搜索到類名稱空間。1
2
3
4
5print(s1.x)??#?對(duì)象屬性333,搜索到對(duì)象名稱空間print(s1.y)??#?對(duì)象屬性444,搜索到對(duì)象名稱空間print(s1.z)??#?對(duì)象屬性555,搜索到對(duì)象名稱空間s1.m()???????#?搜索到類名稱空間s1.n()???????#?搜索到類名稱空間
對(duì)象與對(duì)象之間的名稱空間是完全隔離的,對(duì)象與類之間的名稱空間存在連接關(guān)系。所以,s1和s2中的x和y和z是互不影響的,誰(shuí)也看不見(jiàn)誰(shuí)。
但現(xiàn)在想要訪問(wèn)類變量x、y,而不是對(duì)象變量,該怎么辦?直接通過(guò)類名的完全限定方式即可:1
2print(s1.x)??????#?輸出333,對(duì)象屬性,搜索到對(duì)象名稱空間print(supcls.x)??#?輸出3,類屬性,搜索到類名稱空間
和類一樣,通過(guò)對(duì)象可以訪問(wèn)到方法,方法可以賦值給一個(gè)變量保存下來(lái),以后再執(zhí)行:1
2mm?=?s1.m
mm()
實(shí)際上,s1.m是一個(gè)function類的實(shí)例對(duì)象。在s1.xxx搜索索性的時(shí)候,如果發(fā)現(xiàn)xxx不是對(duì)象或類的數(shù)據(jù)屬性,說(shuō)明這是方法屬性,python會(huì)將這個(gè)方法與對(duì)象(obj.method)或類(cls.method)進(jìn)行關(guān)聯(lián)。無(wú)論如何,方法都是類的屬性。
因?yàn)閷?duì)象有了自己的名稱空間,就可以直接向這個(gè)名稱空間添加屬性或設(shè)置屬性。例如,下面為s1對(duì)象添加一個(gè)新的屬性,但并不是在類內(nèi)部設(shè)置,而是在類的外部設(shè)置:1
2s1.x?=?3333???????#?在外部設(shè)置已有屬性xs1.var1?=?"aaa"???#?在外部添加新屬性var1
新屬性var1將只存在于s1,不存在于s2和類supcls中。
類屬性和對(duì)象屬性
屬于類的屬性稱為類屬性,即那些存在于類名稱空間的屬性。類屬性分為類變量和類方法。有些類方法無(wú)法通過(guò)對(duì)象來(lái)調(diào)用,這類方法稱為稱為靜態(tài)方法。
類似的,屬于對(duì)象名稱空間的屬性稱為對(duì)象屬性。對(duì)象屬性脫離類屬性,和其它對(duì)象屬性相互隔離。
例如:1
2
3
4
5
6
7class?cls:
x=3
def?f():
y=4
print(y)????def?m(self):????????self.z=3
上面的x、f、m都是類屬性,x是類變量,f和m是類方法,z是對(duì)象屬性。x可以通過(guò)類名和對(duì)象名來(lái)訪問(wèn)。
f沒(méi)有參數(shù),不能通過(guò)對(duì)象來(lái)調(diào)用(通過(guò)對(duì)象調(diào)用時(shí)默認(rèn)會(huì)傳遞對(duì)象名作為方法的第一個(gè)參數(shù)),只能通過(guò)類名來(lái)調(diào)用,所以f屬于靜態(tài)方法。
m可以通過(guò)對(duì)象名來(lái)調(diào)用,也可以通過(guò)類名來(lái)調(diào)用(在子類繼承父類,擴(kuò)展父類方法的時(shí)候很常用)。
z通過(guò)self設(shè)置,獨(dú)屬于每個(gè)self參數(shù)代表的對(duì)象,所以是對(duì)象屬性。
子類繼承時(shí)的名稱空間
子類和父類之間有繼承關(guān)系,它們的名稱空間也通過(guò)一種特殊的方式進(jìn)行了連接:子類可以繼承父類的屬性。
例如下面的例子,子類class childcls(supcls)表示childcls繼承了父類supcls。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class?supcls():
x?=?3
y?=?4
def?m(self):
x?=?33
self.x?=?333
self.y?=?444
self.z?=?555
def?n(self):????????return?self.x,?self.y,?self.zclass?childcls(supcls):
y?=?supcls.y?+?1????#?通過(guò)類名訪問(wèn)父類屬性
def?n(self):????????self.z?=?5555
當(dāng)python解釋完這兩段代碼塊時(shí),初始時(shí)的名稱空間結(jié)構(gòu)圖如下:
當(dāng)執(zhí)行完class childcls(supcls)代碼塊之后,子類childcls就有了自己的名稱空間。初始時(shí),這個(gè)名稱空間中除了連接到父類supcls外,還有自己的類變量y和方法n(),子類中的方法n()重寫了父類supcls的方法n()。
因?yàn)橛凶约旱拿Q空間,所以可以訪問(wèn)類屬性。當(dāng)訪問(wèn)的屬性不存在于子類中時(shí),將自動(dòng)向上搜索到父類。1
2
3print(childcls.x)???#?父類屬性,搜索到父類名稱空間print(childcls.y)???#?子類自身屬性,搜索到子類名稱空間print(childcls.z)???#?錯(cuò)誤,子類和父類都沒(méi)有該屬性
當(dāng)創(chuàng)建子類對(duì)象的時(shí)候,子類對(duì)象的變量搜索規(guī)則:子類對(duì)象自身名稱空間
子類的類名稱空間
父類的類名稱空間
例如,創(chuàng)建子類對(duì)象c1,并調(diào)用子類的方法n():1
2c1?=?childcls()
c1.n()
現(xiàn)在,子類對(duì)象c1、子類childcls和父類supcls的關(guān)系如下圖所示:
通過(guò)前面的說(shuō)明,想必已經(jīng)不用過(guò)多解釋。
多重繼承時(shí)的名稱空間
python支持多重繼承,只需將需要繼承的父類放進(jìn)子類定義的括號(hào)中即可。1
2
3
4
5
6
7
8class?cls1():
...class?cls2():
...class?cls3(cls1,cls2):
...
上面cls3繼承了cls1和cls2,它的名稱空間將連接到兩個(gè)父類名稱空間,也就是說(shuō)只要cls1或cls2擁有的屬性,cls3構(gòu)造的對(duì)象就擁有(注意,cls3類是不擁有的,只有cls3類的對(duì)象才擁有)。
但多重繼承時(shí),如果cls1和cls2都具有同一個(gè)屬性,比如cls1.x和cls2.x,那么cls3的對(duì)象c3.x取哪一個(gè)?會(huì)取cls1中的屬性x,因?yàn)橐?guī)則是按照(括號(hào)中)從左向右的方式搜索父類。
再考慮一個(gè)問(wèn)題,如果cls1中沒(méi)有屬性x,但它繼承自cls0,而cls0有x屬性,那么,c3.x取哪個(gè)屬性。
這在python 3.x中是一個(gè)比較復(fù)雜的問(wèn)題,涉及到動(dòng)態(tài)調(diào)整訪問(wèn)順序。但基本上可以總結(jié)為:先左后右,先深度再?gòu)V度,但必須遵循共同的超類最后搜索。
此處只給一個(gè)他們的訪問(wèn)順序圖示,后面會(huì)專門寫一篇關(guān)于多重繼承相關(guān)的文章。
類自身就是一個(gè)全局屬性
在python中,類并沒(méi)有什么特殊的,它存在于模塊文件中,是全局名稱空間中的一個(gè)屬性。
例如,在模塊文件中定義了一個(gè)類cls,那么這個(gè)cls就是一個(gè)全局變量,只不過(guò)這個(gè)變量中保存的地址是類代碼塊所在數(shù)據(jù)對(duì)象。1
2
3#?模塊文件頂層class?cls():
n?=?3
而模塊本身是一個(gè)對(duì)象,有自己的模塊對(duì)象名稱空間(即全局名稱空間),所以類是這個(gè)模塊對(duì)象名稱空間中的一個(gè)屬性,僅此而已。
另外需要注意的是,類代碼塊和函數(shù)代碼塊不一樣,涉及到類代碼塊中的變量搜索時(shí),只會(huì)根據(jù)對(duì)象與類的連接、子類與父類的繼承連接進(jìn)行搜索。不會(huì)像全局變量和函數(shù)一樣,函數(shù)內(nèi)可以向上搜索全局變量、嵌套函數(shù)可以搜索外層函數(shù)。
例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#?全局范圍x?=?3def?f():
print(x)???#?搜索到全局變量xclass?sup():
#?print(x)???#?這是錯(cuò)的,不會(huì)搜索全局變量
y?=?3
print(y)?????#?這是對(duì)的,存在類屬性y
def?m(self):
#?print(y)???#?這是錯(cuò)的,不會(huì)搜索到類變量
self.z?=?4class?childcls(sup):
#?print(y)??????#?這是錯(cuò)的,不會(huì)搜索到父類
其實(shí)很容易理解為什么面向?qū)ο笠凶约旱乃阉饕?guī)則。對(duì)象和類之間是is a的關(guān)系,子類和父類也是is a的關(guān)系,這兩個(gè)is a是面向?qū)ο髸r(shí)名稱空間之間的連接關(guān)系,在搜索屬性的時(shí)候可以順著"這根樹"不斷向上爬,直到搜索到屬性。
__dict__就是名稱空間
前面一直說(shuō)名稱空間,這個(gè)抽象的東西用來(lái)描述作用域,比如全局作用域、本地作用域等等。
在其他語(yǔ)言中可能很難直接查看名稱空間,但是在python中非常容易,因?yàn)橹灰菙?shù)據(jù)對(duì)象,只要有屬性,就有自己的__dict__屬性,它是一個(gè)字典,表示的就是名稱空間。__dict__內(nèi)的所有東西,都可以直接通過(guò)點(diǎn)"."的方式去訪問(wèn)、設(shè)置、刪除,還可以直接向__dict__中增加屬性。
例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class?supcls():
x=3class?childcls(supcls):
y=4
def?f(self):????????self.z=5>>>?c=childcls()>>>?c.__dict__.keys()
dict_keys([])>>>?c.f()>>>?c.__dict__{'z':?5}
可以直接去增、刪、改這個(gè)dict,所作的修改都會(huì)直接對(duì)名稱空間起作用。1
2
3
4>>>?c.newkey?=?"NEWKEY">>>?c.__dict__["hello"]?=?"world">>>?c.__dict__{'z':?5,?'newkey':?'NEWKEY',?'hello':?'world'}
注意,__dict__表示的是名稱空間,所以不會(huì)顯示類的屬性以及父類的屬性。正如上面剛創(chuàng)建childcls的實(shí)例時(shí),dict中是空的,只有在c.f()之后才設(shè)置獨(dú)屬于對(duì)象的屬性。
如果要顯示類以及繼承自父類的屬性,可以使用dir()。
例如:1
2
3
4
5
6
7>>>?c1?=?childcls()>>>?c1.__dict__{}>>>?dir(c1)
['__class__',?'__delattr__',?'__dict__',
......'f',?'x',?'y']
關(guān)于__dict__和dir()的詳細(xì)說(shuō)明和區(qū)別,參見(jiàn)dir()和__dict__的區(qū)別。
__class__和__base__
前面多次提到對(duì)象和類之間有連接關(guān)系,子類與父類也有連接關(guān)系。但是到底是怎么連接的?對(duì)象與類之間,通過(guò)__class__進(jìn)行連接:對(duì)象的__class__的值為所屬類的名稱
子類與父類之間,通過(guò)__bases__進(jìn)行連接:子類的__bases__的值為父類的名稱
例如:1
2
3
4
5
6
7
8
9class?supcls():
x=3class?childcls(supcls):
y=4
def?f(self):????????self.z=5c?=?childcls()
c是childcls類的一個(gè)實(shí)例對(duì)象:1
2>>>?c.__class__
childcls繼承自父類supcls,父類supcls繼承自祖先類object:1
2
3
4
5>>>?childcls.__bases__(,)>>>?supcls.__bases__(,)
查看類的繼承層次
下面通過(guò)__class__和__bases__屬性來(lái)查看對(duì)象所在類的繼承樹結(jié)構(gòu)。
代碼如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def?classtree(cls,?indent):
print("."?*?indent?+?cls.__name__)????for?supcls?in?cls.__bases__:
classtree(supcls,?indent?+?3)def?objecttree(obj):
print("Tree?for?%s"?%?obj)
classtree(obj.__class__,?3)class?A:?passclass?B(A):?passclass?C(A):?passclass?D(B,?C):?passclass?E:?passclass?F(D,?E):?passobjecttree(B())
print("==============")
objecttree(F())
運(yùn)行結(jié)果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16Tree?for?<__main__.B?object?at?0x037D1630>...B......A.........object==============Tree?for?<__main__.F?object?at?0x037D1630>...F......D.........B............A...............object.........C............A...............object......E.........object
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的python类的构造方法名称_搞懂Python的类和对象名称空间的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 利用python进行数据分析论文_利用P
- 下一篇: python 字符串 编码 解码_Pyt