前言
作為一名python的腦殘粉,請先跟我念一遍python大法好。
其作為動態語言的靈活,簡介的程式碼,確實在某些情況下確實比其他程式語言要好。但你有沒有想過,有時這些靈活的語法,可能會造成一些糟糕的體驗。尤其是針對新手,python易上手不假,但動態語言寫得專案規模一大,其實比相對嚴謹的靜態語言,更考驗程式設計師的內力。
哪怕你只是用過python寫過一些初等的專案,那你可能也體驗過以下這種情況。
- 莫名其妙的傳錯引數型別。
python不需要顯式宣告引數型別,同樣,什麼樣的變數都可以往函式裡扔,包括函式(python支援函數語言程式設計),這容易出現一個問題,如果一個函式不是自己設計的,你很可能網裡面傳了錯誤型別的引數。當然,這樣大多數觸發異常,因為傳錯型別意味著函式的一些操作該型別不支援。但有時傳了錯誤的引數型別,卻並不會觸發異常(比如字串相加和數字相加,以及一切物件的==判斷),不會觸發異常結果卻是錯誤的,這就意味著出現了問題更難確定位置,甚至如果這個函式的返回值,再傳進其他函式(假設叫B)時,當你發現得到結果錯誤時,你很可能以為是B函式的邏輯設計出現了錯誤,從而花費大量的時間在錯誤的地方,使用python多數是對開發效率比較重要的場景,而很可能因為一個粗心,使得寫程式碼的時間短了,結果將時間都花在找BUG上了。 - 進行操作之前忘記了轉型
典型的就是把引數型別為str
與int
相加。或者把str
傳進range()
裡面。 - 列表越界
python的列表類似於動態陣列,沒有長度的限制。雖然大多數我們只需用for x in one_list
即可完成對列表的訪問,而不需要去考慮列表的長度。但其實還有一種情景,比如說一個列表(或者元組)中元素的次序是有意義的,比如說[name,age,sex]
而且這可能是某個函式動態生成的,比如爬蟲爬取網站後從裡面挑選資訊後返回,這時,如果這個網站中age,sex的資訊缺失,python可不會自動補充None下去,至少我沒看見有人的函式或方法會考慮到這一點,而是直接給你返回[name]
,而如果你需要獲取age,而直接訪問下標為1的元素,則會觸發異常。
類似的還有種種,當然並非不可解決,比如足夠多的assert
和isinstance
,足夠嚴謹的邏輯設計,枯燥但很有必要的單元測試等等……但使用python很多時候就是為了加快開發效率,上面的這些措施顯然太過麻煩。
斷斷續續的寫了一兩天,弄了幾個裝飾器來解決這些問題,下面開始分享一下。
什麼是裝飾器
需要了解pythopn中裝飾器的基本概念,可以參考一下廖老師的py教程
點這裡
如何利用裝飾器限制函式的引數和返回值
使用裝飾器可以使得一個函式外面加上某些操作然後在重新返回到你定義的函式名字指定的物件上。
說實話,我很難用言語描述出這種關係,直接上程式碼好了。
以使用裝飾器限制函式引數型別為例:
裝飾器的實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def type_limit(*typeLimit,**returnType): def test_value_type(func): def wrapper(*param,**kw): length = len(typeLimit) if length != len(param): raise LimitError("The list of typeLimit and param must have the same length") for index in range(length): t = typeLimit[index] p = param[index] if not isinstance(p,t): raise LimitError("The param %s should be %s,but it's %s now!"%(str(p),type(t()),type(p))) res = func(*param,**kw) if "returnType" in returnType: limit = returnType["returnType"] if not isinstance(res,limit): raise LimitError("This function must return a value that is %s,but now it's %s"%(limit,type(res) ) ) return res return wrapper return test_value_type |
假設我希望實現一個函式,實現兩數求和,為了避免傳進去的是兩個字串,造成字串連線,我需要限制其型別都為int
這時,我們可以這麼做:
1 2 3 |
@type_limit(int,int) def test(x,y): return x+y |
這個定義的過程發生了什麼呢?上述程式碼等價於
1 2 |
temp = type_limit(int,int) #temp = test_value_type test = temp(test) #這是,test已經在原test上經過修飾,指向wrapper |
而在wrapper中,最終會返回撥用原test的結果,這個裝飾器做的,只不過是在呼叫原test前,利用
isinstance
進行了一遍型別檢測而已。這樣,我們可以簡單的模仿像java
,C++
這樣的靜態語言一樣,在宣告的時候就對引數型別進行限制了。
理解這個裝飾器把握著一下幾點:
- 函式可以作為變數使用,即可以作為引數和返回值
- 裝飾器利用了函式內的函式,可以訪問外層函式之間的一些變數從而對內層函式進行修飾。(比如對將要傳進內層函式的引數進行檢測等),從而實現對引數的型別進行限制。
理解這兩點後,你可以自由的修改和擴充這些裝飾器,如果你有更好的實現,記得在github
上pull
給我哦,github
地址稍後給出。
其他相關的限制
除上述外,我還是實現了其他限制:
- 列表長度的限制,不足指定長度,自動補充指定元素。
- 對二維列表的每一維列表進行長度限制,不足指定長度,自動補充指定元素。主要為某些演算法進行限制。
- 常量類Const,目測沒有什麼用
- 對列表的每一元素的型別進行限制
後記
限於篇幅,其他的程式碼不一一在這裡介紹,關鍵思路在上文已給出,其餘程式碼開源在github上,如果需要,你可以直接拿去使用。不過記得不要濫用。
如有更好的建議和或不正確的地方,可以在本文或github
下告知。
如有錯別字……請忽略(^ ^)