Python:类型注解、inspect
发布日期:2021-05-07 14:08:02 浏览次数:17 分类:原创文章

本文共 6459 字,大约阅读时间需要 21 分钟。

目录

函数定义的弊端

Python是动态语言,变量随时可以被赋值,且能赋值为不同的类型

Python不是静态编译型语言,变量类型是在运行器决定的

动态语言很灵活,但是这种特性也是弊端

def  add(x,y):    return x + yprint(add(1,2))print(add("Hello","Word"))print(add(1,"Word"))   #Python是强类型这里会报错
  • 难发现:由于不做任何类型检查,直到运行期问题才显现出来,或者线上运行时才能暴露出问 题
  • 难使用:函数的使用者看到函数的时候,并不知道你的函数的设计,并不知道应该传入什么类 型的数据

如何解决这种动态语言定义的弊端尼

1、 增加文档Documentation String

  • 函数提供说明文档【但是函数定义更新了,文档未必同步更新】
def  add(x,y):    '''    :param int:x:     :param int:y:     :return int:     '''    return x + yprint(add(1,2))print(add("Hello","Word"))print(add(1,"Word"))

2、函数注解

  •  Python 3.5引入的
  • 对函数的参数进行类型注解
  • 对函数的返回值进行类型注解
  • 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
  • 提供给第三方工具,做代码分析,发现隐藏的bug
  • 函数注解的信息,保存在__annotations__属性

3、变量注解

  • Python 3.6引入【i : int = 3】

 业务应用

函数参数类型检查

  • 函数参数的检查,一定是在函数外
  • 函数应该作为参数,传入到检查函数中
  • 检查函数拿到函数传入的实际参数,与形参声明对比
  • __annotations__属性是一个字典,其中包括返回值类型的声明。假设要做位置参数的判断,无 法和字典中的声明对应。使用inspect模块

inspet模块

  • 提供获取对象信息的函数,可以检查函数和类、类型检查

 

inspect模块

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 inspectdef add(x, y:int=7, *args, z, t=10,**kwargs) -> int:    return x + ysig = inspect.signature(add)print(sig)print('params : ', sig.parameters)  # 有序字典 params :  OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y: int = 7">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)])print('return : ', sig.return_annotation)     #return :  <class 'int'>print('~~~~~~~~~~~~~~~~')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')结果:(x, y: int = 7, *args, z, t=10, **kwargs) -> intparams :  OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y: int = 7">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)])return :  <class 'int'>~~~~~~~~~~~~~~~~1 x <class 'inspect._empty'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>True2 y <class 'int'> POSITIONAL_OR_KEYWORD 7False3 args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'>True4 z <class 'inspect._empty'> KEYWORD_ONLY <class 'inspect._empty'>True5 t <class 'inspect._empty'> KEYWORD_ONLY 10False6 kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'>True

signature(callable),获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它 所在的类和名称空间及其他信息

import inspectdef add(x:int, y:int, *args,**kwargs) -> int:    return x + ysig = inspect.signature(add)print(sig, type(sig)) # 函数签名                          #(x: int, y: int, *args, **kwargs) -> int <class 'inspect.Signature'>print('params : ', sig.parameters)                      # OrderedDict  params :  OrderedDict([('x', <Parameter "x: int">), ('y', <Parameter "y: int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])print('return : ', sig.return_annotation)               #return :  <class 'int'>print(sig.parameters['y'], type(sig.parameters['y']))   #y: int    <class 'inspect.Parameter'>print(sig.parameters['x'].annotation)                   # <class 'int'>print(sig.parameters['args'])                           # *argsprint(sig.parameters['args'].annotation)                #<class 'inspect._empty'>print(sig.parameters['kwargs'])                         # **kwargsprint(sig.parameters['kwargs'].annotation)              #<class 'inspect._empty'>

inspect模块is
 

返回的True或False

print(inspect.isfunction(add))          #是否是函数print(inspect.ismethod(add))            #判断是否类的方法print( inspect.isgenerator(add))        #是否是生成器对象print(inspect.isgeneratorfunction(add)) #是否是类print(inspect.ismodule(inspect))        #是否是模块print(inspect.isbuiltin(print))         #是否是内建对象

业务中适合使用


如:请检查用户输入是否符合参数注解的要求

  • 调用时,判断用户输入的实参是否符合要求
  • 调用时,用户感觉还是在调用add函数
  • 对用户输入的数据和声明的类型进行对比,如果不符合,提示用户
def add(x:int,y:int=7) -> int:    return x + y #print(add(7))#print(add(4,y=8))#print(add(x=4,y=8))#print(add(4,y=8))#print(add("end","is"))

判断代码

import inspectimport functoolsdef check(fn):    @functools.wraps(fn)    def wrapper(*args,**kwargs):        print(args,kwargs)   #接受到传入的参数        sig = inspect.signature(fn)        print(sig)        print('params : ', sig.parameters)  # 有序字典        print('return : ', sig.return_annotation)        params = sig.parameters        #位置参数        params_list = list(params.keys())        tmp_list = [0]*len(params_list)        for i ,v in enumerate(args):            k = params_list[i]            tmp_list[i] = 1            if isinstance(v, params[k].annotation) :                print(v, "is", params[k].annotation)            else:                errstr = "{} {} {}".format(v, "not is", params[k].annotation)                raise TypeError(errstr)        #关键字处理        if len(kwargs) !=0:            for k,v in kwargs.items():               if isinstance(v,params[k].annotation):                   print(v,"is",params[k].annotation)               else:                    errstr = "{} {} {}".format(v, "not is", params[k].annotation)                    raise TypeError(errstr)        else:            for k,v in enumerate(tmp_list):                if tmp_list[k] == 0:                    print("{}".format(params_list[k]),"is default ",params[params_list[k]].default)        ret = fn(*args,**kwargs)        return ret    return wrapper@checkdef add(x:int, y:int=7) -> int:    return x + y#print(add(7))#print(add(4,y=8))#print(add(x=4,y=8))#print(add(4,y=8))#print(add("end","is"))  #不可调用,检查失败

对业务稍加变形

def add(x,y:int=7) -> int:    return x + y 
import inspectdef check(fn):    def wrapper(*args, **kwargs):        sig = inspect.signature(fn)        params = sig.parameters        values = list(params.values())        for i,p in enumerate(args):            param = values[i]            if param.annotation is not param.empty and not isinstance(p, param.annotation):                print(p,'!==',values[i].annotation)                break        for k,v in kwargs.items():            if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):                print(k,v,'!===',params[k].annotation)                break        return fn(*args, **kwargs)    return wrapper@checkdef add(x, y:int=7) -> int:    return x + y#print(add(10,20))#print(add(x=10,y=20))#print(add(10,y=20))print(add("end","class"))


 

上一篇:Python:面向对象
下一篇:Python:funtools模块、覆盖被装饰的函数模块名、名称、限定名、文档、参数注解

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2025年03月23日 04时38分12秒