在Python 3中實現型別檢查器

PyPer發表於2015-05-25

示例函式

為了開發型別檢查器,我們需要一個簡單的函式對其進行實驗。歐幾里得演算法就是一個完美的例子:

在上面的示例中,引數 a 和 b 以及返回值應該是 int 型別的。預期的型別將會以函式註解的形式來表達,函式註解是 Python 3 的一個新特性。接下來,型別檢查機制將會以一個裝飾器的形式實現,註解版本的第一行程式碼是:

使用“gcd.__annotations__”可以獲得一個包含註解的字典:

需要注意的是,返回值的註解儲存在鍵“return”下。這是有可能的,因為“return”是一個關鍵字,所以不能用作一個有效的引數名。

檢查返回值型別

返回值註解儲存在字典“__annotations__”中的“return”鍵下。我們將使用這個值來檢查返回值(假設註解存在)。我們將引數傳遞給原始函式,如果存在註解,我們將通過註解中的值來驗證其型別:

我們可以用“a”替換函式gcd的返回值來測試上面的程式碼:

由上面的結果可知,確實檢查了返回值的型別。

檢查引數型別

函式的引數存在於關聯程式碼物件的“co_varnames”屬性中,在我們的例子中是“gcd.__code__.co_varnames”。元組包含了所有區域性變數的名稱,並且該元組以引數開始,引數數量儲存在“co_nlocals”中。我們需要遍歷包括索引在內的所有變數,並從引數“args”中獲取引數值,最後對其進行型別檢查。

得到了下面的程式碼:

在上面的迴圈中,i是陣列args中引數的以0起始的索引,arg是包含其值的字串。可以利用“f.__code__.co_varnames[i]”讀取到引數的名稱。型別檢查程式碼與返回值型別檢查完全一樣(包括錯誤訊息的異常)。

為了對關鍵字引數進行型別檢查,我們需要遍歷引數kwargs。此時的型別檢查幾乎與第一個迴圈中相同:

得到的裝飾器程式碼如下:

將型別檢查程式碼寫成一個函式將會使程式碼更加清晰。為了簡化程式碼,我們修改錯誤資訊,而當返回值是無效的型別時,將會使用到這些錯誤資訊。我們也可以利用 functools 模組中的 wraps 方法,將包裝函式的一些屬性複製到 wrapper 中(這使得 wrapper 看起來更像原來的函式):

結論

註解是 Python 3 中的一個新元素,本文例子中的使用方法很普通,你也可以想象很多特定領域的應用。雖然上面的實現程式碼並不能滿足實際產品要求,但它的目的本來就是用作概念驗證。可以對其進行以下改善:

  • 處理額外的引數( args 中意想不到的專案)
  • 預設值型別檢查
  • 支援多個型別
  • 支援模板型別(例如,int 型列表)

相關文章