天天看點

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:

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:

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_only,keyword-only參數,對應*或*args之後出現的非可變關鍵字參數;

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

positional_or_keyword,var_positional,keyword_only,var_keyword,參數類型(形參)可用此判斷;

實參的資料類型用annotation判斷;

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

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'>

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

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

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

例(參數檢查):

import inspect

from functools import wraps

def check(fn):

    @wraps(fn)

    def wrapper(*args,**kwargs):

        print(args,kwargs)   #此處不可以**kwargs,print函數中沒有類似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]   #用key找key,技巧

            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():   #關鍵字參數傳參處理

        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)

16類型注解

注:

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

視圖,一般隻讀;

pycharm裡抽取函數,選中内容-->refactor-->extract-->method

        values = list(params.values())

        for i,p in enumerate(args):

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

                print('==')

        for k,v in kwargs.items():

                print('===')

        return fn(*args,**kwargs)

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

#add(4,y=2)

add(4)

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

    def wrapper(*args,**kwargs):

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

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

add('mag','edu')

param.empty與inspect._empty一樣;

繼續閱讀