Python入門篇-類型注解


              Python入門篇-類型注解

                                      作者:尹正傑

版權聲明:原創作品,謝絕轉載!否則將追究法律責任。

 

 

 

一.函數定義的弊端

1>.動態語言很靈活,但是這種特性也是弊端

  Python是動態語言,變量隨時可以被賦值,且能賦值為不同的類型
  Python不是靜態編譯型語言,變量類型是在運行器決定的
  動態語言很靈活,但是這種特性也是弊端
    def add(x, y):
      return x + y
    print(add(4, 5))
    print(add('hello', 'world'))
    add(4, 'hello')   難發現:由於不做任何類型檢查,直到運行期問題才顯現出來,或者線上運行時才能暴露出問題
  難使用:函數的使用者看到函數的時候,並不知道你的函數的設計,並不知道應該傳入什么類型的數據

2>.如何解決這種動態語言定義的弊端

增加文檔Documentation String
  這只是一個慣例,不是強制標准,不能要求程序員一定為函數提供說明文檔
  函數定義更新了,文檔未必同步更新
    def add(x, y):
      '''
      :param x: int
      :param y: int
      :return: int
    '''
    return x + y
    print(help(add))

3>.函數注解

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 def add(x:int , y:int) -> int :
 9     '''
10     :param x: int
11     :param y: int
12     :return: int
13     '''
14     return x + y
15 
16 print(help(add))
17 print(add(4, 5))
18 print(add('Yin', 'zhengjie'))
19 
20 
21 
22 
23 #以上代碼執行結果如下:
24 Help on function add in module __main__:
25 
26 add(x:int, y:int) -> int
27     :param x: int
28     :param y: int
29     :return: int
30 
31 None
32 9
33 Yinzhengjie

 

 

二.函數注解Function Annotations

1>.函數注解

  Python 3.5引入
  對函數的參數進行類型注解
  對函數的返回值進行類型注解
  只對函數參數做一個輔助的說明,並不對函數參數進行類型檢查
  提供給第三方工具,做代碼分析,發現隱藏的bug
  函數注解的信息,保存在__annotations__屬性中

2>.變量注解

Python 3.6引入變量注解
  i : int = 3

 

三.業務應用-函數參數類型檢查

1>.思路

  函數參數的檢查,一定是在函數外
  函數應該作為參數,傳入到檢查函數中
  檢查函數拿到函數傳入的實際參數,與形參聲明對比
  __annotations__屬性是一個字典,其中包括返回值類型的聲明。假設要做位置參數的判斷,無法和字典中的聲明對應。使用inspect模塊

2>.inspet模塊

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import inspect
 8 """
 9 signature(callable):
10     獲取簽名(函數簽名包含了一個函數的信息,包括函數名、它的參數類型、它所在的類和名稱空間及其他信息)
11 """
12 
13 def add(x:int, y:int, *args,**kwargs) -> int:
14     return x + y
15 
16 print(add.__annotations__)                 #不推薦使用它,返回的結果是無序的
17 
18 sig = inspect.signature(add)
19 print(sig, type(sig))                        # 函數簽名
20 print('params : ', sig.parameters)          # OrderedDict,即返回的結果是一個有序字典
21 print('return : ', sig.return_annotation)
22 print(sig.parameters['y'], type(sig.parameters['y']))
23 print(sig.parameters['x'].annotation)
24 print(sig.parameters['args'])
25 print(sig.parameters['args'].annotation)
26 print(sig.parameters['kwargs'])
27 print(sig.parameters['kwargs'].annotation)
28 print("是否是函數:{}".format(inspect.isfunction(add)))
29 print("是否是類的方法:{}".format(inspect.ismethod(add)))
30 print("是否是生成器對象:{}".format(inspect.isgenerator(add)))
31 print("是否是生成器函數:{}".format(inspect.isgeneratorfunction(add)))
32 print("是否是類:{}".format(inspect.isclass(add)))
33 print("是否是模塊:{}".format(inspect.ismodule(inspect)))
34 print("是否是內建對象:{}".format(inspect.isbuiltin(print)))            #還有很多is函數,需要的時候查閱inspect模塊幫助
35 
36 
37 
38 
39 #以上代碼執行結果如下:
40 {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
41 (x:int, y:int, *args, **kwargs) -> int <class 'inspect.Signature'>
42 params :  OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
43 return :  <class 'int'>
44 y:int <class 'inspect.Parameter'>
45 <class 'int'>
46 *args
47 <class 'inspect._empty'>
48 **kwargs
49 <class 'inspect._empty'>
50 是否是函數:True
51 是否是類的方法:False
52 是否是生成器對象:False
53 是否是生成器函數:False
54 是否是類:False
55 是否是模塊:True
56 是否是內建對象:True
 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import inspect
 8 """
 9 Parameter對象
10     保存在元組中,是只讀的
11     name,參數的名字
12     annotation,參數的注解,可能沒有定義
13     default,參數的缺省值,可能沒有定義
14     empty,特殊的類,用來標記default屬性或者注釋annotation屬性的空值
15     kind,實參如何綁定到形參,就是形參的類型
16         POSITIONAL_ONLY,值必須是位置參數提供
17         POSITIONAL_OR_KEYWORD,值可以作為關鍵字或者位置參數提供
18         VAR_POSITIONAL,可變位置參數,對應*args
19         KEYWORD_ONLY,keyword-only參數,對應*或者*args之后的出現的非可變關鍵字參數
20         VAR_KEYWORD,可變關鍵字參數,對應**kwargs
21 """
22 
23 def add(x, y:int=7, *args, z, t=10,**kwargs) -> int:
24     return x + y
25 
26 sig = inspect.signature(add)
27 print(sig)
28 print('params : ', sig.parameters) # 有序字典
29 print('return : ', sig.return_annotation)
30 print("*" * 20 + "我是分割線" + "*" * 20)
31 
32 for i, item in enumerate(sig.parameters.items()):
33     name, param = item
34     print(i+1, name, param.annotation, param.kind, param.default)
35     print(param.default is param.empty, end='\n\n')
36 
37 
38 
39 #以上代碼執行結果如下:
40 (x, y:int=7, *args, z, t=10, **kwargs) -> int
41 params :  OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y:int=7">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)])
42 return :  <class 'int'>
43 ********************我是分割線********************
44 1 x <class 'inspect._empty'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>
45 True
46 
47 2 y <class 'int'> POSITIONAL_OR_KEYWORD 7
48 False
49 
50 3 args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'>
51 True
52 
53 4 z <class 'inspect._empty'> KEYWORD_ONLY <class 'inspect._empty'>
54 True
55 
56 5 t <class 'inspect._empty'> KEYWORD_ONLY 10
57 False
58 
59 6 kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'>
60 True
Parameter對象

3>.小試牛刀

有函數如下
  def add(x, y:int=7) -> int:
    return x + y
  
請檢查用戶輸入是否符合參數注解的要求?

思路
  調用時,判斷用戶輸入的實參是否符合要求
  調用時,用戶感覺上還是在調用add函數
  對用戶輸入的數據和聲明的類型進行對比,如果不符合,提示用戶
 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import inspect
 8 
 9 def check(fn):
10     def wrapper(*args, **kwargs):
11         sig = inspect.signature(fn)
12         params = sig.parameters
13         values = list(params.values())
14         for i,p in enumerate(args):
15             param = values[i]
16             if param.annotation is not param.empty and not isinstance(p, param.annotation):
17                 print(p,'!==',values[i].annotation)
18         for k,v in kwargs.items():
19             if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):
20                 print(k,v,'!===',params[k].annotation)
21         return fn(*args, **kwargs)
22     return wrapper
23 
24 @check
25 def add(x, y:int=7) -> int:         #我們要求第二個參數必須是int類型,並且返回值類型也為int
26     return x + y
27 
28 print(add(2,1))
29 print(add(20,y=10))
30 print(add(y=100,x=200))
31 print(add("Yin","zhengjie"))        #我們在實際傳參時故意不按照要求傳參,發現會有相應的提示信息
32 
33 
34 
35 #以上代碼執行結果如下:
36 3
37 30
38 300
39 zhengjie !== <class 'int'>
40 Yinzhengjie

 


免責聲明!

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



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