16類型註解

 

python類型註解

 

函數定義的弊端:

python是動態語言,變量隨時可以被賦值,且能賦值爲不同的類型;

python不是靜態編譯型語言,變量類型是在運行時決定的;

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

難發現,由於不做任何類型檢查,直到運行時問題才顯現出來,或在線上才能暴露出問題;

難使用,函數的使用者看到函數時,並不知道設計者是如何設計的函數,也不知道應該傳入什麼類型的數據;

 

例:

In [63]: add(4,5)

Out[63]: 9

In [64]: add('hello','world')

Out[64]: 'helloworld'

In [65]: add(4,'hello')   #強弱類型語言的區別舉例

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-65-d5d4453c2cd4> in <module>()

----> 1 add(4,'hello')

<ipython-input-62-c1dcfb42218b> in add(x, y)

      1 def add(x,y):

----> 2     return x+y

TypeError: unsupported operand type(s) for +: 'int' and 'str'

 

解決函數定義的弊端:

Documentation String

只是一個慣例,不是強制標準,不能要求程序員一定爲函數提供說明文檔;

函數定義更新了,文檔未必同步更新;

 

例:

In [67]: def add(x,y):

    ...:     '''

    ...:     :param x: int

    ...:     :param y: int

    ...:     :return: int

    ...:     '''

    ...:     return x+y

    ...:

In [68]: help(add)

Help on function add in module __main__:

add(x, y)

    :param x: int

    :param y: int

    :return: int

(END)

 

 

 

function annotation函數註解:

java註解是兩碼事;

python3.5引入;

對函數的參數進行類型註解;

對函數的返回值進行類型註解;

只對函數參數作一個輔助說明,並不對函數參數進行類型檢查;

提供給第三方工具,作代碼分析,發現隱藏的bug

函數註解的信息保存在__annotations__屬性中,如add.__annotations__

 

變量註解:

python3.6引入;

 

業務應用:

函數參數類型檢查;

思路:

函數參數的檢查,一定是在函數外;

函數應該作爲參數,傳入到檢查函數中(裝飾器);

檢查函數拿到函數傳入的實際參數,與形參聲明對比;

__annotations__屬性是一個字典,包括函數參數及返回值的聲明,是普通字典(非有序字典),假設要做位置參數的判斷,無法和此字典中的聲明對應,要使用inspect模塊;

 

例:

In [71]: def add(x:int,y:int)->int:

    ...:     '''

    ...:     :param x: int

    ...:     :param y: int

    ...:     :return: int

    ...:     '''

    ...:     return x+y

    ...:

In [73]: add.__annotations__   #普通字典,而非有序字典

Out[73]: {'return': int, 'x': int, 'y': int}

 

 

 

inspect模塊:

提供獲取對象信息的函數,可以檢查函數和類、類型檢查;

inspect.signature(callable),獲取簽名,函數簽名包含了一個函數的信息,包括函數名、函數參數、缺省值、返回值,它的參數類型,它所在的類,和名稱空間及其它信息;

inspect.isfunction(add),是否是函數,限定只是函數,函數在類中爲method

inspect.ismethod(add),是否是類的方法;

inspect.isgenerator(add),是否是生成器對象;

inspect.isgeneratorfunction(add),是否是生成器函數;

inspect.isclass(add),是否是類;

inspect.ismodule(inspect),是否是模塊;

inspect.isbuiltin(print),是否是內建對象;

 

例:

In [74]: import inspect

In [75]: def add(x:int,y:int,*args,**kwargs)->int:

    ...:     return x+y

    ...:

In [76]: add.__annotations__   #是普通字典,順序隨機

Out[76]: {'return': int, 'x': int, 'y': int}

In [77]: sig=inspect.signature(add)

In [78]: sig   #函數簽名,聲明是什麼樣,即函數第一行,定義時的東西

Out[78]: <Signature (x:int, y:int, *args, **kwargs) -> int>

In [79]: print('params:',sig.parameters)   #OrderedDict有序字典,解決了調用時傳參的順序問題,可迭代,迭代中的每一個元素爲parameter

params: OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])

In [80]: print('return:',sig.return_annotation)

return: <class 'int'>

In [81]: sig.parameters['x']

Out[81]: <Parameter "x:int">

In [82]: sig.parameters['x'].annotation

Out[82]: int

In [83]: sig.parameters['y']

Out[83]: <Parameter "y:int">

In [84]: sig.parameters['y'].annotation

Out[84]: int

In [85]: sig.parameters['args']

Out[85]: <Parameter "*args">

In [86]: sig.parameters['args'].annotation

Out[86]: inspect._empty

In [87]: sig.parameters['kwargs']

Out[87]: <Parameter "**kwargs">

In [88]: sig.parameters['kwargs'].annotation

Out[88]: inspect._empty

 

parameter對象:

保存在元組中,是隻讀的;

name,參數的名字;

default,參數的缺省值,可能沒有定義;

annotation,參數的註解,可能沒有定義;

empty,特殊的類,用來標記default屬性或annotation屬性的空值,與sig.parameters['x'].annotation是一個東西;

kind,實參如何綁定到形參,就是形參的類型:

POSITIONAL_ONLY,值必須是位置參數提供,python中未實現此項,僅常量定義了;

POSITIONAL_OR_KEYWORD,值可以作爲關鍵字或位置參數提供;

VAR_POSITIONAL,可變位置參數,對應*args

KEYWORD_ONLYkeyword-only參數,對應**args之後出現的非可變關鍵字參數;

VAR_KEYWORD,可變關鍵字參數,對應**kwargs

 

POSITIONAL_OR_KEYWORDVAR_POSITIONALKEYWORD_ONLYVAR_KEYWORD,參數類型(形參)可用此判斷;

實參的數據類型用annotation判斷;

 

例:

In [93]: def add(x,y:int=7,*args,z,t=10,**kwargs)->int:

    ...:     return x+y

    ...:

In [94]: sig=inspect.signature(add)

In [95]: sig

Out[95]: <Signature (x, y:int=7, *args, z, t=10, **kwargs) -> int>

In [96]: sig.parameters

Out[96]:

mappingproxy({'args': <Parameter "*args">,

              'kwargs': <Parameter "**kwargs">,

              't': <Parameter "t=10">,

              'x': <Parameter "x">,

              'y': <Parameter "y:int=7">,

              'z': <Parameter "z">})

In [97]: sig.return_annotation

Out[97]: int

In [98]: print(sig.return_annotation)

<class 'int'>

In [99]: for i,(name,param) in enumerate(sig.parameters.items()):

    ...:     print(i+1,name,param.annotation,param.kind,param.default)

    ...:     print(param.default is param.empty,end='\n\n')

    ...:    

1 x <class 'inspect._empty'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>

True

 

2 y <class 'int'> POSITIONAL_OR_KEYWORD 7

False

 

3 args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'>

True

 

4 z <class 'inspect._empty'> KEYWORD_ONLY <class 'inspect._empty'>

True

 

5 t <class 'inspect._empty'> KEYWORD_ONLY 10

False

 

6 kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'>

True

 

例(參數檢查):

import inspect

from functools import wraps

 

def check(fn):

    @wraps(fn)

    def wrapper(*args,**kwargs):

        print(args,kwargs)   #此處不可以**kwargsprint函數中沒有類似y=7關鍵字參數

        sig = inspect.signature(fn)

        print(sig)

        print('params:',sig.parameters)

        print('return:',sig.return_annotation)

        print('~~~~~~~~~~~~~~~~~~~~~~~')

#         for i,(name,param) in enumerate(sig.parameters.items()):

#             print(i+1,name,param.name,param.annotation,param.kind,param.default)

#             print(param.default is param.empty,end='\n\n')

#         for param in sig.parameters.values():

#             print(param.name,param)

#             print(param.name,param.annotation,param.kind,param.default)

        params = sig.parameters

   

        param_list = list(params.keys())

        for i,v in enumerate(args):   #位置參數傳參處理

            k = param_list[i]   #keykey,技巧

            if isinstance(v,params[k].annotation):

                print(v,'is',params[k].annotation)

            else:

#                 print(v,'is not',params[k].annotation)

                errstr = '{} is not {}'.format(v,params[k].annotation)

                print(errstr)

                raise TypeError(errstr)

               

        for k,v in kwargs.items():   #關鍵字參數傳參處理

            if isinstance(v,params[k].annotation):

                print(v,'is',params[k].annotation)

            else:

#                 print(v,'is not',params[k].annotation)

                errstr = '{} is not {}'.format(v,params[k].annotation)

                print(errstr)

                raise TypeError(errstr)

 

        ret = fn(*args,**kwargs)

        return ret

    return wrapper

 

@check

def add(x:int,y:int=7)->int:

    return x + y

 

#add(4,8)

#add(x=4,y=8)

#add(4,y=8)

#add('mag','edu')

add(x='mag',y='edu')

#add(4)

#add(4,8,y=8)

1.jpg

注:

MappingProxyType,有序字典被包裝過(虛的,假的);

視圖,一般只讀;

pycharm裏抽取函數,選中內容-->Refactor-->Extract-->Method

 

例:

from functools import wraps

import inspect

 

def check(fn):

    @wraps(fn)

    def wrapper(*args,**kwargs):

        sig = inspect.signature(fn)

        params = sig.parameters

        values = list(params.values())

        for i,p in enumerate(args):

            if isinstance(p,values[i].annotation):

                print('==')

        for k,v in kwargs.items():

            if isinstance(v,params[k].annotation):

                print('===')

        return fn(*args,**kwargs)

    return wrapper

 

@check

def add(x:int,y:int=7):

    return x + y

 

#add(4,8)

#add(x=4,y=8)

#add(4,y=2)

add(4)

####################

from functools import wraps

import inspect

 

def check(fn):

    @wraps(fn)

    def wrapper(*args,**kwargs):

        sig = inspect.signature(fn)

        params = sig.parameters

        values = list(params.values())

        for i,p in enumerate(args):

#             if isinstance(p,values[i].annotation):

#                 print('==')

            param = values[i]

            if param.annotation is not param.empty and not isinstance(p,param.annotation):

                print(p,'!=',values[i].annotation)

        for k,v in kwargs.items():

#             if isinstance(v,params[k].annotation):

#                 print('===')

            if params[k].annotation is not inspect._empty and not isinstance(v,params[k].annotation):

                print(v,'!==',params[k].annotation)

        return fn(*args,**kwargs)

    return wrapper

 

@check

def add(x:int,y:int=7):

    return x + y

 

#add(4,8)

#add(x=4,y=8)

#add(4,y=2)

#add(4)

add('mag','edu')

 

注:

param.emptyinspect._empty一樣;

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章