前言
許多小伙伴已經可以使用 Python/ target=_blank class=infotextkey>Python 解決小問題。定義幾個變量,洋洋灑灑寫幾段 if 和 for 都不喘氣。
但是,面對一個稍微復雜的問題,總感覺哪里不對勁,好像代碼怎么樣都寫不出來規整的樣子。
是不是沒有"面向對象"?
是不是要定義一些類,搞一些模塊?
今天,我們就來探討一下,為什么需要定義類。
它到底解決了什么問題,與函數有什么不一樣。
函數就是編寫行動計劃
我們用一個文件數據拆分小需求作為例子。
需求很簡單,把一個數據表,按指定字段把數據拆分到不同的表,過程中需要去掉一些無用的列和行。
如下代碼:
寫出這段代碼的你很開心,運行后也能出來正確結果。
后來,又需要完成一樣的需求,但是文件不一樣,字段也不一樣。
你發現這份代碼的小問題。于是,你把經常需要變化的變量,寫到最上方,以便每次可以更容易找到并修改它們。
這時候的你不知不覺學會了定義函數。稍微改造一下:
函數就像在寫明天的工作計劃,編寫的時候是不執行。
此時的參數叫做形參,因為這時候它們沒有具體的值。 當執行函數的時候,需要指定一個確定值給這些參數,所以這時候參數叫實參。
這些知識很簡單,但是此時你必須要注意到,我們的代碼組織上,有了巨大的變化——數據以及處理這些數據的相關邏輯,被封裝到一個范圍內。
接下來,需求要升級一下
類,另一種組織方式
因為執行過程中去掉了一些行,有時候你希望能夠把這些記錄單獨輸出文件,方便查看。
那不就又要多一個參數,用來指定輸出文件名字或路徑嗎?
你開始意識到這個函數的參數有點多。因為它負責的事情太多了!
于是,你按流程順序,拆分成不同的小函數。
現在執行的代碼成這樣子:
- 每個函數的輸入,來自于上一個函數的輸出
如何進行劃分,是面向過程和面向對象的重要區別。這里為了便于理解,簡單按流程劃分。
現在加入需求"把過程中刪除掉的行單獨輸出"的函數:
這實現方式你很滿意,因為它負責的事情只有一件,所以實現代碼足夠簡單
但你卻發現了問題,因為加入到執行代碼的時候:
新函數的第一個參數的實際值(實參),不知道在哪里。因為,它需要上一個函數"配合",返回去掉的數據。
于是,第二個函數要修改它的返回值才能滿足:
重點:
這種使用函數,面向流程的組織方式就有這種缺點。因為每個小函數之間通過返回值強關聯,同時需求變化很容易導致返回值變化,甚至是執行順序的變化。
怎么解決?
為了讓大家更容易理解,我一步步來推演。
首先,想辦法干掉每個小函數之間的數據依賴。既然每個小函數都可能返回不同的東西,我們直接用一個容器去存放它們。
- 在執行流程開始之前,定義一個空字典
- 每執行一個小函數,必須傳入這個字典
- 小函數不需要返回結果數據,有需要保留數據,就直接寫入到這個字典中
此時,每個小函數的第一個參數都是字典:
現在函數之間數據的依賴關系,由原來的
變成
現在我們已經很接近定義類!
但是,現在數據字典是一個外部的變量,如果需要同時處理多個操作,數據很容易亂套。
而且,每個小函數的第一個參數都是這個數據字典,不太好看。
下面我用一種 python 中很不常見的寫法解決:
為了方便查看,省略了2個函數。
重點:
- 行3:定義一個函數 new
- 行4:數據字典
- 行6-11:原來的小函數搬到這里而已。但是要注意,它們第一個參數不需要設置 data_dict
- 行7:利用閉包,這些小函數可以直接訪問行4定義的字典
- 行13-16:以字典方式,把這些函數返回出去
執行的代碼現在成這樣子:
可以在 new 函數中設定參數,用于保存過程中需要的數據
當然,這種方式有一些缺點,比如執行的時候無法得到智能提示,因為現在函數需要從字典中取出。但是它解決了之前說的流程數據依賴問題。
這種方式可以讓你理解帶數據狀態的執行流程:每次要執行,都需要調用函數 new 。這個函數做了什么?就是創建了一個數據字典和一系列操作這個數據字典的相關函數
如果你看到這里,那么恭喜你,你已經學會了 python 中定義類。
我們用定義類的代碼如下:
- 行4:語法規則而已,相當于定義了函數 new 的名字
- 行5-6:這叫初始化函數,相當于函數 new 的執行過程。注意第一個參數 self,其實它就是相當于之前說的數據字典
- 行8:定義函數,注意它現在第一個參數是數據字典。只不過在執行的時候,python 會自動為我們傳遞數據字典
- 行9:self.data ,類似之前數據字典用法:data_dict['data'] =xxx
- 行14:同樣道理,self.data 可以訪問數據字典 data 的對應值
python 還會把我們定義的函數,加入到數據字典中。也就是說,每個小函數都可以通過 self.函數名() ,調用彼此
執行時候的代碼:
- 第一句,我們稱為實例化對象。本質就是構造了一個全新的數據字典,通過這個字典,我們可以訪問相關的邏輯處理函數
是不是與之前我們用數據字典很類似。因為它們的內在本質是一樣。
類有繼承等特性。不過我們同樣可以通過構造數據字典的方式做到。
不要忘記一鍵三連。你的點贊、收藏、關注,是我創作的動力。
- Python進階系列:Python遍歷的秘密
- 多了解Python一點點,為什么我們需要定義變量?
- 為什么你總是學不會Python,入門Python的4大陷阱