利用裝飾器給python的函數加上類型限制

前言

做爲一名python的腦殘粉,請先跟我念一遍python大法好。java

其做爲動態語言的靈活,簡介的代碼,確實在某些狀況下確實比其餘編程語言要好。但你有沒有想過,有時這些靈活的語法,可能會形成一些糟糕的體驗。尤爲是針對新手,python易上手不假,但動態語言寫得項目規模一大,其實比相對嚴謹的靜態語言,更考驗程序員的內力。python

哪怕你只是用過python寫過一些初等的項目,那你可能也體驗過如下這種狀況。git

  • 莫名其妙的傳錯參數類型。
    python不須要顯式聲明參數類型,一樣,什麼樣的變量均可以往函數裏扔,包括函數(python支持函數式編程),這容易出現一個問題,若是一個函數不是本身設計的,你極可能網裏面傳了錯誤類型的參數。固然,這樣大多數觸發異常,由於傳錯類型意味着函數的一些操做該類型不支持。但有時傳了錯誤的參數類型,卻並不會觸發異常(好比字符串相加和數字相加,以及一切對象的==判斷),不會觸發異常結果倒是錯誤的,這就意味着出現了問題更難肯定位置,甚至若是這個函數的返回值,再傳進其餘函數(假設叫B)時,當你發現獲得結果錯誤時,你極可能覺得是B函數的邏輯設計出現了錯誤,從而花費大量的時間在錯誤的地方,使用python多數是對開發效率比較重要的場景,而極可能由於一個粗心,使得寫代碼的時間短了,結果將時間都花在找BUG上了。程序員

  • 進行操做以前忘記了轉型
    典型的就是把參數類型爲strint相加。或者把str傳進range()裏面。github

  • 列表越界
    python的列表相似於動態數組,沒有長度的限制。雖然大多數咱們只需用for x in one_list便可完成對列表的訪問,而不須要去考慮列表的長度。但其實還有一種情景,好比說一個列表(或者元組)中元素的次序是有意義的,好比說[name,age,sex]並且這多是某個函數動態生成的,好比爬蟲爬取網站後從裏面挑選信息後返回,這時,若是這個網站中age,sex的信息缺失,python可不會自動補充None下去,至少我沒看見有人的函數或方法會考慮到這一點,而是直接給你返回[name],而若是你須要獲取age,而直接訪問下標爲1的元素,則會觸發異常。算法

相似的還有種種,固然並不是不可解決,好比足夠多的assertisinstance,足夠嚴謹的邏輯設計,枯燥但頗有必要的單元測試等等……但使用python不少時候就是爲了加快開發效率,上面的這些措施顯然太過麻煩。編程

斷斷續續的寫了一兩天,弄了幾個裝飾器來解決這些問題,下面開始分享一下。數組

什麼是裝飾器

須要瞭解pythopn中裝飾器的基本概念,能夠參考一下廖老師的py教程
點這裏app

如何利用裝飾器限制函數的參數和返回值

使用裝飾器可使得一個函數外面加上某些操做而後在從新返回到你定義的函數名字指定的對象上。編程語言

說實話,我很難用言語描述出這種關係,直接上代碼好了。

以使用裝飾器限制函數參數類型爲例:

裝飾器的實現以下:

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

這時,咱們能夠這麼作:

@type_limit(int,int)
def test(x,y):
    return x+y

這個定義的過程發生了什麼呢?上述代碼等價於

temp = type_limit(int,int) #temp =  test_value_type
test = temp(test) #這是,test已經在原test上通過修飾,指向wrapper

而在wrapper中,最終會返回調用原test的結果,這個裝飾器作的,只不過是在調用原test前,利用
isinstance進行了一遍類型檢測而已。這樣,咱們能夠簡單的模仿像javaC++這樣的靜態語言同樣,在聲明的時候就對參數類型進行限制了。

理解這個裝飾器把握着一下幾點:

  • 函數能夠做爲變量使用,便可以做爲參數和返回值

  • 裝飾器利用了函數內的函數,能夠訪問外層函數之間的一些變量從而對內層函數進行修飾。(好比對將要傳進內層函數的參數進行檢測等),從而實現對參數的類型進行限制。

理解這兩點後,你能夠自由的修改和拓展這些裝飾器,若是你有更好的實現,記得在githubpull給我哦,github地址稍後給出。

其餘相關的限制

除上述外,我仍是實現了其餘限制:

  • 列表長度的限制,不足指定長度,自動補充指定元素。

  • 對二維列表的每一維列表進行長度限制,不足指定長度,自動補充指定元素。主要爲某些算法進行限制。

  • 常量類Const,目測沒有什麼用

  • 對列表的每一元素的類型進行限制

後記

限於篇幅,其餘的代碼不一一在這裏介紹,關鍵思路在上文已給出,其他代碼開源在github上,若是須要,你能夠直接拿去使用。不過記得不要濫用。

github地址

若有更好的建議和或不正確的地方,能夠在本文或github下告知。

若有錯別字……請忽略(^ ^)

相關文章
相關標籤/搜索