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一样;


猜你喜欢

转载自blog.51cto.com/jowin/2388550