函數定義的弊端
- python 是動態語言,變量隨時可以被賦值,且能賦值為不同類型
- python 不是靜態編譯型語言,變量類型是在運行器決定的
- 動態語言很靈活,但是這種特性也是弊端
def add(x,y): return x+y print(add(4,5)) print(add('hello','world')) print(add(4,'hello')) #報錯,TypeError: unsupported operand type(s) for +: 'int' and 'str'
- 難發現:由於不做任何類型檢查,直到運行期問題才顯現出來,或者線上運行時才能暴露出問題
- 難使用:函數的使用者看到函數的時候,並不知道你的函數的設計,並不知道應該傳入什么類型數據
如何解決這種動態語言定義的弊端呢?
- 增加文檔Docmentation String
- 這是一個慣例,不是強制標准,不能要求程序員一定為函數提供說明文檔
- 函數定義更新了,文檔未必同步更新
def add(x,y): """ :param x:int :param y:int :return:int """ return x+y print(help(add))
- 函數注解
- python3.5引入
- 對函數的參數進行類型注解
- 對函數的返回值進行類型注解
- 只對函數參數做一個輔助說明,並不對函數參數進行類型檢查
- 提供給第三方工具,做代碼分析,發現隱形bug
- 函數注解的信息,保存在__annotations__屬性中
- python3.6中引入變量注解
- i:int = 3
def add(x:int,y:int)->int: """ :param x:int :param y:int :return:int """ return x+y print(help(add)) print(add(4,5)) print(add("jax","zhai")) #在pycharm里參數是灰色
函數參數類型檢查
思路:
- 函數參數的檢查,一定是在函數外
- 函數應該作為參數,傳入到檢查函數中
- 檢查函數拿到函數傳入的實際參數,與形參聲明對比
- __annotations__屬性是一個字典,其中包括返回值類型的聲明,假設要做一個位置參數的判斷,無法和字典中的聲明對應,使用inspect模塊
- inspect模塊
- 提供獲取對象信息的函數,可以檢查函數和類、類型檢查
inspect模塊
- signature(callable),獲取簽名(函數簽名包含一個函數的信息,包括函數名、它的參數類型、它所在的類和名稱空間及其他信息)
import inspect def add(x:int,y:int,*args,**kwargs)->int: return x+y sig = inspect.signature(add) print(sig,type(sig)) #獲取簽名 print('params :',sig.parameters) #OrderedDict print('return :',sig.return_annotation) #返回值類型 print(sig.parameters['y'], type(sig.parameters['y'])) print(sig.parameters['x'].annotation) print(sig.parameters['args']) print(sig.parameters['args'].annotation) print(sig.parameters['kwargs']) print(sig.parameters['kwargs'].annotation)
inspect.isfunction(add) #是不是函數 inspect.ismethod(add) #是不是類方法 inspect.isgenerator(add) #是不是生成器 inspect.isclass(add) #是不是類 inspect.ismodule(add) #是不是模塊 inspect.isbuiltin(add) #是不是內建對象
- Parameter對象
- 保存在元組中,是只讀的
- name ,參數的名字
- annotation 參數注解,可能沒有定義
- default 參數的缺省值,可能沒有定義
- empty 特殊的類,用來標記default屬性或者注釋annotation屬性的空值
- kind 實參如何綁定到形參,就是形參的類型
- POSITIONAL_ONLY,值必須是位置參提供
- POSITIONAL_OR_KEYWORD,值可以作為關鍵字或者位置參數提供
- VAR_POSITIONAL,可變位置參數,對應*args
- KEYWORD_ONLY,keyword-only參數,對應*或者*args之后的出現的非可變關鍵字參數
- VAR_KEYWORD,可變關鍵字參數,對應**kwargs
舉例:
import inspect def add(x:int,y:int,*args,**kwargs)->int: return x+y sig = inspect.signature(add) print(sig) print('params : ', sig.parameters) print('return : ', sig.return_annotation) print('~~~~~~~~~~~~~~~~') for i,item in enumerate(sig.parameters.items()): name,param = item print(i+1,name,param.annotation,param.kind,param.default) print(param.default is param.empty, end = '\n\n')
有函數如下:
def add(x,y:int=7) -> int: return x + y
- 檢查用戶輸入是否符合參數注解的要求?
- 思路:
- 調用時,判斷用戶輸入的實參是否符合要求
- 調用時,用戶感覺上還是在調用add函數
- 對用戶輸入的數據和聲明的類型進行對比,如果不符合,提示用戶
def check(fn): def wrapper(*args,**kwargs): sig = inspect.signature(fn) params = sig.parameters values = list(params.values()) #print(values) for i,p in enumerate(args): # print(i,p) params = values[i] #print(params.annotation,params.empty) if params.annotation is not params.empty and not isinstance(p,params.annotation): print(p,'!==',values[i].annotation) for k,v in kwargs.items(): if sig.parameters[k].annotation is not inspect._empty and not isinstance(v,sig.parameters[k].annotation): print(k,v,'!===',sig.parameters[k].annotation) return fn(*args,**kwargs) return wrapper @check def add(x,y:int=7) -> int: return x + y #print(add(20,10)) print(add(20,y=10)) print(add(y=10,x=20))