一、python中類和對象的概念
首先,我們先來說說什么是類。看了很多關於python類的介紹,大多都介紹如何使用,但是對於概念卻一筆帶過,一個初學編程的小伙伴很難理解。
概括的說:類可以比作是某種類型集合的描述。然而這中抽象的描述,對於初學者來說並沒有什么卵用。
今天就來詳細的說說:
我們把一類相同的事物叫做類,其中用相同的屬性(其實就是變量)描述,里面封裝了相同的方法。比如,汽車是一個類,它包括價格、品牌等屬性。那么我們需要打印某一輛車的價格和品牌,只需要使用一句代碼 print "the car's type ‘ford’,price:280000",但是當我們需要對一百個品種的車打印這句話的時候,怎么辦呢?
這個問題我們通過以前學過的函數式編程就可以實現啦!我們只需要寫一個函數,然后將不同的車品牌和價格以參數的方式傳到函數里就好了。這樣大大的提高了代碼的重用性,我們就不需要把同樣的print這句話寫100次了。
但是同樣的功能,我們用類也是可以實現的,怎么實現呢?先上一張圖,再來講具體的。這里只需要大致知道這兩種實現方式其實實現的功能是一樣的就可以了。

我們先忽略類里面那些沒見過的鬼東西,只是把這兩段代碼都分別拿到python里去執行,我們發現實現的功能是一樣的。這說明了什么呢?其實類能實現的功能,函數幾乎都可以實現,我們都知道C語言中也是沒有對象的,但是它還是很膩害的寫出了linux操作系統!!!知道了這一點,我們就知道,類是讓我們的程序錦上添花的產物,並沒那么可怕。


接下來我們來解析這段內容,左右兩邊的代碼是完全一樣的,右邊是加了解釋的。
這里結合代碼來解釋什么叫做類和對象,我們知道,ford和passat都是一種車,不同的車又有不同的品牌、價格。
所以在這里,“car”就是“類”,表示“車”這一類事物,它有很多屬性,比如型號、價格等等。而passat和ford都是車的一種,它是具體的,有固定的品牌和價格,所以passat是car的一個對象,ford是car的另一個對象。
二、python類中的函數和普通函數的對比
從剛剛的講解中,我們知道,類能實現的功能我們用函數也完全可以實現,但是我們可以看到他們的實現方式是不同的,現在我們就逐一對比一下。先上圖!

上圖中的printCarInfo和CarInfo兩個方法都實現了同樣的功能,那么他們的差別在哪里呢?
1.我們看到兩個方法的縮進不同,printCarInfo這個方法是被包裹在car這個類里面的
2.兩個函數的參數是不同的,CarInfo方法很直接的傳了cartype和price這兩個參數,而在printCarInfo方法中只傳遞了一個self。這也直接導致了后面在函數中使用變量的時候也是不同的。
三、python類語法的初識
使用類中的方法
CarInfo中的方法我們都知道是什么了,那么printCarInfo里面傳遞的這個‘self’是個什么鬼?

當當當,揭曉答案,這里的self就表示對象。看上面的圖,我們是怎么調用car類中的printCarInfo方法的?好像和我們之前調用普通方法不一樣了,前面加了一個passat和ford(使用了car的對象),而且后面根本就沒有加參數,就可以在方法中直接打印出cartype和price兩個變量的值。magic!這又是為什么呢?
這里我們再來鞏固一下類和對象的概念,car是一個類,它擁有品牌和價格兩個屬性;passat和ford是兩個品牌的車,是車的一種,那么他們就擁有車的屬性:品牌和價格,不僅擁有,他們的屬性還是具體的、有值得,比如passat的品牌是passat,價格就是250000。
所以當我們使用passat這個對象去調用printCarInfo這個方法的時候,其實是做了這樣一部操作printCarInfo(passat),把passat這個對象傳給了printCarInfo方法,passat這個對象又包含了兩個屬性cartype、price,我們在python規定這樣使用一個對象中的變量:passat.cartype、passat.price。不要問為什么,這是語法,你這么寫python就認識,不這么寫它就不認識!
此時我們就可以整理一下思路了:我們調用函數時傳的passat對象的參數passat.cartype、passat.price被類中printCarInfo函數以self的身份接收,所以我們在使用參數的時候自然就變成了self.cartype、self.price。
類的初始化
但是計算機很傻,你要想使用這兩個變量,不能上來就用呀,得先告訴python每個對象對應的屬性,我們叫做對象的初始化,如下圖:

其實這兩句話每句話都完成了兩個功能:第一,從car類中實例化出了一個對象——passat/ford;第二:給新對象的屬性賦了值。其實passat = Car('passat',25000)還可以寫成這樣:


像上左圖這樣寫也許比較有助於我們理解類和對象,我把以前的寫法放在右側作對比。我們讓passat = Car(),就是創建了car的一個對象,不要問為什么,這是語法!后面的兩句話就是給passat的兩個屬性賦值,哈哈,感覺有點兒像字典的。就像passat = {‘cartype’:‘passat’,‘price’:250000},只不過表現方式不同,而且類的作用可不僅僅是對對象的抽象,還封裝了方法啦!呃,扯遠了。。。現在我們仔細觀察一下上左圖的代碼就會發現,下面初始化和賦值過程的寫法變了,init方法也和右圖不同,以前方法的三個參數只剩下了一個self,這是因為我們在創建對象的時候:passat = Car(),只是做了這樣一件事Car(passat),把對象傳給了類,並沒有進行賦值,所以我們看init方法里,這個時候passat對象的的cattype和price都是空值,直到后面賦值之后,passat的屬性才有了屬於它的意義。這樣我們也應該知道init是什么了,這個方法就是我們在創建對象的時候會自動執行的方法,當我們通過car類實例化一個對象的時候,程序會自動執行這個類的init方法。然后也會自動的按照init方法中的代碼給這個對象中的屬性賦值。
類中方法的定義和使用
在最開始,我們已經知道使用類的對象就可以調用類中的方法,其實在類中的方法和普通函數的卻別很小,先上圖對比一下:

我們根據上圖來比較,先來看函數的定義,從圖中很明顯就可以看出,CarInfo中的參數在printCarInfo方法中的換成了self,在方法的調用中,就是用self.屬性名來調用了。
再來看方法的調用,普通函數的調用直接調用就可以了,而類中的函數調用之前應該先進行類的實例化——創建一個對象,然后給這個對象的屬性賦值,然后才可以調用類中的方法,調用方式是 對象名.方法名()。
四、類存在的意義及對象的應用
我們在本文的開篇就說過,類能實現的功能,用函數也可以實現,那么類的存在還有什么特殊的意義呢?我們都知道函數存在的意義是為了避免代碼的重用,我們舉個栗子:我有一輛車passat,你有一輛車ford,他們都有計算里程這個功能,因此我們寫了一個方法,叫做“DriveDistance”,passat和ford都可以調用它,那么問題來了,我們怎么知道是誰跑了多少里程呢?從函數的角度來講,我們當然可以自己定義變量來解決這個問題,但是類又是怎么實現的呢?


好了,來看實現,上面是函數方法實現,下面是類方法實現,左圖我畫出了所有的區別,為了看着不鬧心右邊放了原圖對比。先看方法的定義,我們看到橘色框框中標出了兩個方法的參數不同,現在我們已經知道self是對象,在類方法中,對象是必須的參數,因為我們在之前講到過,都是使用 對象名.方法名 調用方法,passat.DriverDistance()的意思是DriverDistance(passat),那么 passat.DriverDistance(20)的意思翻譯過來就是DriverDistance(passat,20),所以無論如何self參數是必須的,這個記着就行。然后我們再來看函數的實現,灰框框里面的內容,是用車從前跑的距離加上新跑的距離就是總共的行駛里程。函數里的實現方法很好理解,我們現在來看看類中的實現方式,是用對象原本的dictance加上新參數的dictance,並賦值給了這個對象的dictance屬性。所以我們看黃色框框里面的內容,在函數的實現中我們需要將函數的執行結果返回調用者,但是在類的實現過程中,卻不需要,因為我們修改的是對象的屬性,在其類方法中修改了,我們再使用的時候就是使用了修改后的值。這里首先解釋了紅框框中不需要返回值的現象,然后解釋了藍框框中在我們打印現在的形式里程的時候,直接用 passat.dictance就可以了。
五、內存眼中的類與對象
類和對象的基礎概念和使用方法到這里就差不多了,然,總會有很多文藝小青年兒在不停的追逐,所以在這里站在內存的角度上來解釋一下,如果對前面的內容還有疑問,請慎入。先上圖!


看上面的圖,左側是代碼,右側是執行結果。一時抽風,把能打印的都打印了,我們挑有意義的來說,首先我們看passat,ford初始化之后,我分別打印了兩個對象的地址,發現兩個對象雖然都是Car實例化的結果,但是實例化之后,都分別有了自己的內存地址,但是在at之前有表明了是car的實例。這說明什么?同一個類實例化出來對象之間的內存是獨立噠,所以passat.cartype和ford.cartype是兩個獨立的變量,他們都和自己的對象放在一起,不會混亂。接下來我們來看這兩個變量調用方法的地址,我們看到at前面的內容都是一樣噠,表示是Car類中的printCarInfo方法,而at后面的內容我們仔細對比之后就可以發現是和前面對象的地址一樣的,那么這個時候我們就知道,盡管函數我們只定義了一個,但是函數確是和對象綁定的,函數里面用到的參數也是對象地址中存儲的參數。這樣看來,其實類也沒有什么難,nie?
作者:心無旁騖_
鏈接:https://www.jianshu.com/p/9492d0668e91
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。