輕松學(xué)會python面向?qū)ο蟮?0篇---方法屬于類,屬性屬于對象
方法屬于類,屬性屬于對象,這并不是一個完全正確的論斷,然而,我還是建議你記住它,理解它,因為以此為起點,可以更好的理解類與對象之間的關(guān)系。

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