python 類型注解


函數定義的弊端

  • 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))

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM