前言: oop非常非常非常重要。搞不懂oop,就玩不了python,就算能寫也一定是寫代碼時候喜歡靠猜瞎貓碰死老鼠寫得心很虛。為什么這么說呢,我也是從面向過程編程到死走過來的,一路def到死,一看到有class的代碼就逃到十萬八千里了不想看,想千方設百計繞過這樣的代碼。不僅不願意看這樣的代碼,而且寫代碼時候很沒思路,老是有一個想找一個原本有的類似的項目或模塊,然后全盤復制過來,在里面進行扣字修改這種沖動。完全從0開始親手寫一個稍微復雜的文件是不存在的,做不到這一點(不知道有沒有人相同的感受)。而且會下載很多pdf教程來學習閱讀,但發現每個教程關於面向對象總會有兩三章的內容,每當看到這些章節教程來了就迅速結束閱讀了,覺得教程上老是花那么多章節介紹面向對象干鳥,我不用面向對象難道不行嗎? 直到認真看了一個同事的代碼和認真看了設計模式和面向對象后,基本上完全無此困惑,基本上做到了從不復制,能夠從容的從0開始寫一個新的py文件,看py教程時候不僅不再非常恐懼面向對象的章節還特意非常喜歡看這些章節,基本上基礎章節都是跳過專門看教程里面的面向對象章節。這就是學習oop和設計模式帶給我最大的幫助,十分收益。10個python有9個不會oop8個從不用oop,這是我從公司人員寫代碼得出的規律,即使是月薪2萬以上的人也是面向過程編程到死。經過對面向過程編程的代碼進程改造,得出的規律是我能輕易使公司的任何模塊和項目的代碼減少40%-90%。同時我們項目也有很多不斷增加的功能和任務,別人一如既往面向過程編程到死,相同的任務,我總是能使用別人五分之一到二分之一的代碼完成別人的全功能,因為代碼少,所以在單位時間能做更多的平台。這些就是oop為什么重要的原因。那么此篇就是介紹,如何輕易使用oop,而不是死記硬背 封裝 繼承 多態 抽象這幾個術語,因為背誦這幾個術語對怎么轉oop沒有太大幫助,只有真正理解后才知道這些術語的含義。
為什么老是不會oop,老是容易陷入面向過程編程到死的窘境?學習oop的難點是 oop不是一種語法,他是一種思維,語法很容易在10分鍾內學會和背誦。思想是抽象的不像語法是具體的,所以這種思維很難學,有的py人員一輩子都不會甚至不打算了解它。
按照四步走的固定公式,能寫模塊就能抽象封裝出一個好用的類。針對只會面向過程編程到死的python人員,按照此公式,能寫模塊就能寫類,能寫項目就能寫框架。
網上講oop概念的很多,但沒抽象出固定公式步驟,看了那些對轉oo不能起到很大指導作用。聽說py簡單,就學py,但py害人就是很松散不是強制oop造成不思進取從不用oop寫法很差,py可以平鋪指令,也可以抽函數,也可以寫面向對象。但大部分是前兩種,只會前兩種寫法,對寫生產項目壞處很大,導致寫新模塊和功能需要復制粘貼扣字,重復代碼很多和調用麻煩和改一次邏輯需要改幾百個地方和不好維護,有這些毛病。oop能使單個模塊代碼行數減少40% - 60%,oop加設計模式能使多個類似模塊的代碼行數減少最高達90%行代碼。重要是不需要復制粘貼扣字幾百次,新功能10行能代替讓來150行,修改一處時候不需要學然來修改幾百次容易改漏改錯,心情很煩躁。以上這些減少的行數比例是我修改純面向過程編程項目的很多個模塊文件后得出的結論。
函數不是也能復用嗎,只有類才能復用嗎,寫類是不是脫了褲子放屁?根據我的經驗是如果問這兩個的區別,那應該是對基本概念都嚴重牛頭不對馬嘴的產生混淆了。函數和類沒有可比性,函數和類里面的方法才有得比。函數復用在不需要依賴外部變量和不需要外部函數(需要動態替換的外部依賴函數)和不需要在一組函數中反復傳參和return結果的時候,才能比較好的效果,如果要修改一個函數所依賴的外部函數不方便,尤其是依賴外部全局變量但又有需要多實例方面的需求困惑,純函數更是難以搞定,能寫但必然寫得很曲折。不是所有東西一定要oop,所以可以根據這條來判斷是否要修改成oop,而不是籠統的聽網上說簡單的用函數復雜的用類,這沒有判斷依據說什么才叫簡單和復雜。同理如果不需要用類的但強行寫類反而是沒有理解透徹面向對象能比原來的寫法帶來什么好處,不管三七二十一什么都寫類我是持反對態度的。面向過程的函數的復用是輸入輸出轉換級別的復用作用是y = f(x),面向對象的類的復用是框架級別復用、流程級別的復用,函數是和方法來比,函數和類比是牛頭不對馬嘴的比較,函數的復用和面向對象級別的復用差遠了。
通常老師在講面向對象時候,會講類和對象的關系,然后接着就是開始定義一個類了,老師會在類里面演示定義幾個屬性。但為什么要在類里面定義幾個屬性(或者叫成員變量)呢,定義他干什么?有什么好處?所有類都不要屬性可以嗎,不要屬性好不好?在面向過程編程時候沒有屬性或者成員變量是怎么繞開解決這個成員變量的? 一般都沒講這幾個原因,導致學生不知道為什么要oop和oop比opp好在哪里能解決什么事情。
除了這個通用公式,那么提高oo和代碼水平的方法是看w3school或者菜鳥教程的設計模式教程。每個月至少看一次36種設計模式,每個月都要看一次。每一種模式都是oop封裝的濃縮精華案例。
1、之前寫的這個很久了
python模塊和類的通用轉換規則(2),三步轉oo
里面提到的是三步走:
模塊和類的轉換規則是:
1、模塊級降為類
2、全局變量改成實例屬性,全局的不會被改變的變量類似於那種const的,可以寫成類屬性(減少點內存存儲可以)。什么時候需要類屬性什么時候需要實例屬性,酌情考慮。
3、然后把函數改成方法。方法是類里面的,函數是模塊里面的。
因為里面舉得一個例子是一個人,但人的屬性寫的是模塊級全局變量,如果是這種寫法,三步走就可以oo了。
2、但現在的情況是很多人不喜歡寫全局變量(不寫全局是為了盡可能模擬多實例,這就不可能用全局變量了,因為全局變量在整個python模塊的命名空間下只有一份;而且是希望從入口函數傳不同的參得到不同的運行結果,不希望是通過修改頂層代碼的全局變量來得到不同的運行結果,所以才會出現盡量少寫全局變量這種現象了),喜歡一步一步一環套一環的傳參和return來處理業務,那么這種寫法,如果僅僅按照上面的三步走戰略,就不夠了,因為只執行這三步后 ,只是有了class外殼,但絲毫沒有封裝的概念,這樣做是沒有個卵用的。我最近就看過一些這樣的類,寫的貌合神離,貌像面向對象,但神是面向過程,這種類當然是十分的沒必要,這種寫法除了在長篇幅的模塊文件中割裂出更小命名空間,使各部分功能緊湊一點以外,因為沒有體現出面向對象帶來的任何優點,也沒任何意義了,直接在這種寫法方式的面向過程的代碼的基礎上,執行這三步加個class關鍵字,只不過是脫了褲子放屁。
如果是這種思維來寫的面向過程,那么三步走還不行,需要重新構思整個流程,即在代碼里面少return少傳參,多用全局變量,按照這個思路在腦袋里面過一遍,這一步是發生在腦袋里面的,不是讓你真在代碼寫全局變量,是為了先構思出來有哪些傳參和return可以弄成全局變量,然后再接着上面的三步走戰略,降級命名空間,就是oo了。
所以這個前置的發生在腦袋里面的過程是不能少的,這一步是整個精神支柱 ,后面三步是修改代碼面貌,腦袋里面不先打好這一步的草稿就匆忙的執行三步走戰略,寫出這種類來,在一些有五六年開發經驗的老手看來,會覺得寫這個python類是在強行裝逼,代碼有點滑稽。有人問是不是代碼里面多寫class關鍵字就是面向對象了?大錯特錯,面向對象oop和代碼有沒有class沒有必然關系,他是一種思維思想不是語法形式,舉例一個使用大家公認的強制面向對象的語言java語言來用面向過程的思維寫代碼的例子,如果也按這種寫法寫python的類,這種就是屬於無效裝逼類。
下面例子使用java,例舉了兩種寫法,一種是使用面向過程,一種是使用面向對象來寫,不用說,代碼肯定有class關鍵字,java沒class不行。
使用100%面向過程的方式來寫java程序
3、那么總結一下就是四步走了:
0)、如果你的代碼已經是按照我的那個人 的那個類那種全局變量加函數的寫法可以跳過這一步。如果是第2種情況完全一環套一環的在每個函數節點里面return加傳參的寫法,那么先需要執行下面這段話。
在你的腦瓜里面構思,你的面向過程的return 傳參,有哪些盡可能多的是可以弄成全局變量的,這時候不用考慮全局變量是不是需要多份的,因為后面在三步走命名空間降級的時候,這個玩意自動變成了實例屬性,而每個類的多個實例的的屬性都是互不干擾的,除非了你刻意寫了單例模式。
1)、模塊級降為類
2)、全局變量改成實例屬性,全局的不會被改變的變量類似於那種const的,可以寫成類屬性(減少點內存存儲可以)。什么時候需要類屬性什么時候需要實例屬性,酌情考慮。
3)、然后把函數改成方法。方法是類里面的,函數是模塊里面的。
第0步發生在腦袋里面,后面三步發生在代碼編輯器里面。跳過第0步直接進行后面三步,那就會寫出面向過程的廢物裝x滑稽類。
面向對象本質是方法和屬性的封裝,如果不允許寫任何一個實例屬性,把所有屬性綁定全部改為外界傳參給方法,所有原來保存中間狀態的實例屬性全部return給外界保存,那寫這樣的類,基本上是沒個鳥用,廢了面向對象的80%功能優點,只不過是加個class外殼罷了。
為什么是廢了面向對象80%優點,面向對象除了封裝不是還有繼承和多態嗎?如果沒有任何實例屬性 ,那么所有方法就沒有需要訪問實例屬性了,所有方法應該聲明為靜態方法,對一組純靜態方法的類玩繼承和多態基本上沒有太多意義,如果能理解這個,就可以想到 隨便查看了100萬個 正確運行的java代碼,可以得出一個規律:一個方法的前綴永遠不可能同時出現abstract 和 static兩個關鍵字,這兩個是關鍵字是互斥的。靜態方法由類名調用,該方法也不訪問任何實例屬性,也就是是靜態方法與對象本身無關,多態時候靜態方法只會使用父類的方法,不使用子類的方法,在一組純靜態方法的工具類的基礎上玩繼承多態基本上是無卵意義,因為沒有卵的必要性,所以直接在語法層面上禁止這兩個關鍵字同時出現,否則會報錯。
4、面向過程人員調用一個類,應該怎么想?
如果還是完全不熟悉類,對模塊加函數更熟悉,那多次實例化一個類到底用熟悉的思維怎么想象他?上面的是說怎么寫類,是模塊降級為類了。調用類時候,思維也可以升級,重新把類在腦瓜里面升級為模塊級寫法,把實例化構造函數傳參想象為修改全局變量,調用方法想象為調用函數。
類可以實例化無數次,完全可以把實例化出來的對象想象為無數個狀態屬性互不干擾的復制的模塊。 所以回到3,就是為什么要寫類而不是寫模塊加全局變量加函數了,模塊是單例,不同的地方使用模塊使用的是模塊的相同的全局變量,小紅用了這個模塊后,修改了全局變量,小明再調用,到底是不是要重置全局變量狀態,不改,小明是女的了,改了小明成男的,但如果其他函數調用了性別全局變量,小紅再調用尿尿函數,小紅就變成男的了結果成了站着尿尿不是蹲着尿尿,因為這就很牟盾,所以要進行降維。類是多實例,模塊是單實例。
5、雖然網上都說面向過程轉面向對象很難,要靠領悟和實踐,沒有捷徑可走。但完成基本的面向對象轉變,可以按照這個公式,嚴格執行四步走戰略。其余的就是多實踐多對比領悟摸索,想玩的精妙需要學習23種設計模式。
6、說完怎么轉oo的公式了,還必須交代為什么這么做呢。
有很多人就信口雌黃堅定並堅持地說python面向對象是廢物沒鳥用,python里面寫class完全沒有一絲必要,說java要寫類那是java語法規定死了必須要寫class,認為python在類里面寫方法和直接在模塊下面寫函數效果完全100%等同,這么個說法是因為只是因為他長期只使用面向過程沒有自定義過一次類,做事只顧完成一個功能,不講究任何維護和擴展,胡說八道了,可以寫函數不是一定要全寫函數,那用c++時候如果不管是什么項目什么場景堅持全寫函數,那要c++這種語言存在干什么,用c就好了。之前舉了很多例子說明oo和op對比的優點,大道理就不用講,直接用一個反證法就能輕易反駁這種說法了,光去看看三方包,有哪個包是純面向過程的?如果真的純面向過程,那個包調用起來肯定非常的苦逼。如果沒用,那發明python的人是吃飽了撐着閑的蛋疼加個類進去,他們為什么不移除面向對象?這么瞎說的人難道是覺得自己已經完全超越了發明python語言的人和所有開發三方庫的人的認知水平了嗎?