在 Python 中,我們經常需要對引數進行校驗,這是我們有好多種方法,例如寫很多 if 啊,或者寫正規表示式啊等等,技巧高的人可以寫得很巧妙,而技巧一般的人呢,可能會寫得很冗長,例如我,經常就不能很好得處理引數校驗的程式碼。
所以我就不斷得尋找,終於最近發現了一個不錯的 python 引數校驗 lib,叫做 voluptuous。名字可能有點難記,而且英文翻譯過來的中文還不怎麼好聽,但是非常好用。下面就逐漸帶大家嘗試一下 voluptuous 庫的妙用。
安裝 voluptuous
要用之前,肯定要先安裝的啦,安裝這一步很簡單,還是按照往常一般使用 pip 搞定。我使用的是當前的最新版 0.8.8:
1 |
pip install voluptuous==0.8.8 |
嘗試 voluptuous
首先,想來一個最為常見的,就是校驗引數的型別,我這裡假設引數都是以 json 格式進行傳遞的,json 格式在 python 中又可以表現為字典(dict),所以這裡就不做區分了。
以官網的日誌為例,使用分頁的引數為例子,校驗引數的型別,首先,我們要求引數的型別,查詢的欄位q
必須為字串,分頁大小per_page
必須為數字,頁碼page
必須為數字。
那麼,使用 voluptuous 後,可以這樣寫:
1 2 3 4 5 6 7 |
from voluptuous import Schema s = Schema({ 'q': str, 'per_page': int, 'page': int }) |
這就表示了我們剛才的需求:查詢的欄位q
必須為字串,分頁大小per_page
必須為數字,頁碼page
必須為數字。
那麼,現在,假設客戶端傳過來了一組引數:
1 2 3 4 |
{"q": "hello", "page": 10, "per_page": 20 } |
那麼,我們要怎麼來校驗呢?也很簡單:
1 2 3 4 5 6 7 8 9 10 11 12 |
from voluptuous import Schema s = Schema({ 'q': str, 'per_page': int, 'page': int }) print s({"q": "hello", "page": 10, "per_page": 20 }) |
這裡可以看到,我們使用客戶端傳遞過來的引數作為引數,呼叫了我們使用既定模板建立的物件,然後直接把他列印出來,如果你跑過這段程式碼,你會發現結果如下:
1 2 3 4 |
{"q": "hello", "page": 10, "per_page": 20 } |
沒錯, voluptuous 就是將校驗通過的引數返回了。就是這麼簡單。
那這時,你可能要問了,如果校驗不通過的引數怎麼辦,我要怎麼處理,下面就演示一下如果傳遞的引數是:
1 2 3 4 |
{"q": "hello", "page": "world", "per_page": 20 } |
這樣的話,要怎麼來處理校驗不通過的問題,其實,當校驗不通過的時候,Schema 會丟擲一個異常,然後我們可以通過捕獲這個異常來確定校驗失敗的問題,例如:
1 2 3 4 |
try: print s({"q": "hello", "page": "world","per_page": 20}) except MultipleInvalid as e: print "error: {} occur while parse args".format(e.errors) |
當你執行這段程式碼之後,你會發現列印出來的內容是:
1 |
error: [TypeInvalid('expected int',)] occur while parse args |
我們就知道引數是有問題的,需要客戶端確認。
更進一步
ok,這就是一個簡單的校驗,下面我們進行更復雜的校驗,我們現在不僅要保證引數的型別正確,我們還需要保證查詢引數一定要有,另外兩個引數可有可無。那麼我們可以怎麼做呢?
其實,也很簡單,就是給必須的引數加上一個 Required 關鍵字,例如這樣:
1 2 3 4 5 6 |
from voluptuous import Schema, Required required_s = Schema({ Required('q'): str, 'per_page': int, 'page': int }) |
然後,我們可以嘗試一下加入什麼都不傳,會發生什麼事:
1 2 3 4 |
try: print required_s({}) except MultipleInvalid as e: print "error: {} occur while parse with required args".format(e.errors) |
很明顯,你跑一遍就知道了,這段程式碼會丟擲這個異常:
1 |
error: [RequiredFieldInvalid('required key not provided',)] occur while parse with required args |
這個時候,我們再貪心一點,我們覺得引數必須還不夠,還不能有多餘的引數,什麼引數都塞過來,浪費我記憶體啊,過濾這個應該和剛才的引數必須有一樣簡單吧?
是的,不過這時因為不是對單獨一個引數起作用了,所以需要放在 Schema 的引數裡面,這個引數叫做 extras,例如下面這段就是表示不要多餘的引數:
1 2 3 4 5 |
not_allow_extra_s = Schema({ 'q': str, 'per_page': int, 'page': int }, extra=False) |
然後我們嘗試一下:
1 2 3 4 |
try: print not_allow_extra_s({"q": "hello", "unknown": "key"}) except MultipleInvalid as e: print "error: {} occur while parse with no extras args".format(e.errors) |
結果也很簡單:
1 |
error: [Invalid('extra keys not allowed',)] occur while parse with no extras args |
其實,Schema 預設就是不允許有多餘引數的,如果我們想要多餘的引數,則需要顯式得將這個引數設定為 True,表示允許有多餘的引數。
現在我們已經知道一些用法了,但是,我們覺得只控制型別不夠,我們還想控制一下引數的長度,例如查詢的引數不能超過 10 個字元,那麼樣怎麼做了?
下面這段程式碼就自定義了一個校驗值不能超過 10 個字元的 str 型別:
1 2 |
def less_than_10(value): if isinstance(value, str) and len(value) |
校驗函式很簡單,接受一個引數,也就是要校驗的值,然後,如果校驗成功就返回這個值,否則就丟擲一個 Invalid 的異常。這樣 Schema 就知道是否校驗通過了。
其實,你可能會想,如果這裡校驗通過後我返回的不是引數的值會怎樣?如果你有這個想法,我很佩服。
確實,如果這裡不返回引數的值,Schema 也會認為是校驗通過的,而且 Schema 會將你的返回值返回。那麼想到這裡,你是不是有一些更加強大的想法?好吧,不知道你有沒有,我這裡告知一下,可以通過這個方法來進行資料轉換。
假如,我們的查詢只支援大寫字母,但是我們認為客戶端傳過來是小寫字母的時候也是有效的,那麼,我們就需要將客戶端傳遞過來的引數進行轉換,轉成大寫字母,那麼,我們可以這樣寫:
1 2 3 4 5 6 7 8 9 10 11 12 |
def convert_letter(value): if isinstance(value, str): return value.upper() raise Invalid("not valid string") transformation_s = Schema({ Required('q'): convert_letter, 'per_page': int, 'page': int }) print transformation_s({'q': 'hello'}) |
然後,我們看一下輸出:
1 |
{'q': 'HELLO'} |
已經變成了大寫了。
好吧,到此已經將 voluptuous 的一些比較常用和重要的功能介紹完了,如果大家有用心去體會的話,相信可以寫出一些非常精妙的校驗器出來。
希望這篇文章對幫助大家理解 voluptuous 有所幫助,謝謝。