輕松學(xué)會(huì)python面向?qū)ο蟮?0篇---方法屬于類,屬性屬于對(duì)象
方法屬于類,屬性屬于對(duì)象,這并不是一個(gè)完全正確的論斷,然而,我還是建議你記住它,理解它,因?yàn)橐源藶槠瘘c(diǎn),可以更好的理解類與對(duì)象之間的關(guān)系。

1. 方法屬于類
如何理解這句話呢,方法的存在,不依賴于對(duì)象,而是依賴于類;屬性的存在,不依賴于類,而是依賴于對(duì)象。
class Dog():
def __init__(self, name):
self.name = name
def biting(self):
print(f'{self.name}在咬人')
def biting_ex(self):
print('咬人')
這是一段簡(jiǎn)單的代碼,定義了一個(gè)Dog類,先來(lái)理解方法屬于類,我增加一行代碼
Dog.biting_ex(None)
這行代碼可以正確運(yùn)行,我傳入?yún)?shù)是None,你可以傳入任意參數(shù),都不是問(wèn)題。我沒(méi)有創(chuàng)建任何實(shí)例對(duì)象,但是我可以調(diào)用實(shí)例方法。
2. 屬性屬于對(duì)象
biting也可以這樣調(diào)用么,我們來(lái)試一下
Dog.biting(None)
結(jié)果報(bào)錯(cuò)了
AttributeError: 'NoneType' object has no attribute 'name'
None沒(méi)有name屬性,屬性依附于對(duì)象而存在,沒(méi)有創(chuàng)建對(duì)象,也就沒(méi)有name屬性,修改一下代碼
dog = Dog('小黑')
Dog.biting(dog)
這樣就沒(méi)問(wèn)題了,Dog.biting(dog) 等價(jià)于dog.biting()。我創(chuàng)建了一個(gè)對(duì)象,name屬性也就存在了,更抽象的說(shuō)法是在內(nèi)存中創(chuàng)建了一個(gè)dog對(duì)象,dog對(duì)象里有一個(gè)name屬性,沒(méi)有創(chuàng)建對(duì)象dog之前,name屬性自然也就不存在。
3. dog.biting與Dog.biting是同一個(gè)東西么
接下來(lái)要講解的,屬于比較深入的內(nèi)容,如果感到吃力,可以放棄
我們通過(guò)查看他們的id來(lái)判斷他們是否是同一個(gè)東西。
dog = Dog('小黑')
print(id(dog.biting))
print(id(Dog.biting))
輸出結(jié)果是
1747144415176
1747147189376
竟然不是同一個(gè)東西,那對(duì)象dog的biting究竟從哪里來(lái)的呢?前面不是剛說(shuō)過(guò)方法是屬于類的么,那么按理說(shuō),對(duì)象所使用的方法應(yīng)該就是類的方法。
這里的確是一個(gè)疑點(diǎn),所以,我們要深入挖掘。
3.1 多個(gè)對(duì)象的biting一樣么
我再創(chuàng)建出一個(gè)對(duì)象,看看多個(gè)對(duì)象的biting是不是同一個(gè)東西
dog = Dog('小黑')
dog1 = Dog('嘿嘿')
print(id(dog.biting)) # 1610069776328
print(id(dog1.biting)) # 1610069776328
不論創(chuàng)建多少個(gè)對(duì)象,他們的biting方法的內(nèi)存地址都是相同的,那么他們從哪里來(lái)的呢,跟Dog.biting到底有沒(méi)有關(guān)系呢,答案是有關(guān)系
3.2 bound method
dog = Dog('小黑')
print(dog.biting)
注意看輸出結(jié)果
<bound method Dog.biting of <__main__.Dog object at 0x00000245E1C3BD30>>
dog.biting 是Dog.biting函數(shù)的綁定方法,雖然不是很容易理解,但是可以明確,他們之間是存在關(guān)系的。
print(dog.__dict__)
print(Dog.__dict__)
我輸出對(duì)象dog和類Dog的__dict__, 可以看到,dog的屬性只有name,而類Dog的屬性有很多,包括了biting。這再次印證我們的觀點(diǎn),方法屬于類,屬性屬于對(duì)象。
結(jié)合bound method 這個(gè)描述,我們推斷biting是一個(gè)描述器,那么dog.biting 就等價(jià)于 Dog.biting.__get__(dog, Dog)),實(shí)驗(yàn)來(lái)證明一切
dog = Dog('小黑')
print(id(dog.biting)) # 1230893225928
print(id(Dog.biting.__get__(dog, Dog))) # 1230893225928
兩次輸出的內(nèi)存地址是一樣的,真想大白于天下。
3.3 事情的真相
對(duì)象dog并沒(méi)有biting屬性,那么在執(zhí)行dog.biting時(shí),對(duì)象的__dict__找不到,就要去其類的__dict__中尋找。
而Dog類中找到的biting是描述器,根據(jù)描述器協(xié)議
self.descr = descr.__get__(self, obj, type=None) --> value
因此dog.biting 就等價(jià)于 biting.__get__(dog, Dog)),返回的是Dog.biting綁定后的方法,
如果事情果真如此,那么我可以將biting方法替換掉
dog = Dog('小黑')
def too_simple():
print("驚不驚喜")
dog.__dict__['biting'] = too_simple
dog.biting() # 驚不驚喜
驚不驚喜,意不意外!能在dog.__dict__找到biting,就不會(huì)從類里尋找了。
3.5 繼續(xù)思考
為什么會(huì)是這樣呢?嗯,這樣很合理。
Dog類只有一個(gè),如果方法屬于對(duì)象,那豈不是有多少個(gè)對(duì)象,就對(duì)應(yīng)著有多少個(gè)biting方法了么,可這些方法是完全一樣的呀,只需要存在一個(gè)就可以了,那么就讓它存在于類中。
屬性呢?一萬(wàn)只狗,就應(yīng)該有一萬(wàn)個(gè)名字啊,因此屬性name應(yīng)當(dāng)是跟隨對(duì)象而存在的。
