python 类型注解

摘要:
想法:调用时,判断用户输入的实际参数是否符合要求。在调用时,用户仍然感觉像调用add函数来将用户输入的数据与声明的类型进行比较。如果没有,将提示用户defcheck:defwrapper:sig=inspect。签名参数=sig。parametersvalues=list#printfori,pinnumerate:#print(i,p)params=values[i]#printifparams。注释不是参数。emptyandnotisinstance:print(p,'!

函数定义的弊端

  • python 是动态语言,变量随时可以被赋值,且能赋值为不同类型
  • python 不是静态编译型语言,变量类型是在运行器决定的
  • 动态语言很灵活,但是这种特性也是弊端
def add(x,y):
    return x+y
print(add(4,5))
print(add('hello','world'))
print(add(4,'hello')) #报错,TypeError: unsupported operand type(s) for +: 'int' and 'str'
  • 难发现:由于不做任何类型检查,直到运行期问题才显现出来,或者线上运行时才能暴露出问题
  • 难使用:函数的使用者看到函数的时候,并不知道你的函数的设计,并不知道应该传入什么类型数据

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

  • 增加文档Docmentation String
    • 这是一个惯例,不是强制标准,不能要求程序员一定为函数提供说明文档
    • 函数定义更新了,文档未必同步更新
def add(x,y):
    """
    :param x:int
    :param y:int
    :return:int
    """
    return x+y
print(help(add))
  • 函数注解
    • python3.5引入
    • 对函数的参数进行类型注解
    • 对函数的返回值进行类型注解
    • 只对函数参数做一个辅助说明,并不对函数参数进行类型检查
    • 提供给第三方工具,做代码分析,发现隐形bug
    • 函数注解的信息,保存在__annotations__属性中
    • python3.6中引入变量注解
      • i:int = 3
def add(x:int,y:int)->int:
    """
    :param x:int
    :param y:int
    :return:int
    """
    return x+y
print(help(add))
print(add(4,5))
print(add("jax","zhai"))   #在pycharm里参数是灰色

函数参数类型检查

思路:

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

inspect模块

  • signature(callable),获取签名(函数签名包含一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息)
import inspect
def add(x:int,y:int,*args,**kwargs)->int:
    return x+y
sig =  inspect.signature(add)
print(sig,type(sig)) #获取签名
print('params :',sig.parameters) #OrderedDict
print('return :',sig.return_annotation) #返回值类型
print(sig.parameters['y'], type(sig.parameters['y']))
print(sig.parameters['x'].annotation)
print(sig.parameters['args'])
print(sig.parameters['args'].annotation)
print(sig.parameters['kwargs'])
print(sig.parameters['kwargs'].annotation)
inspect.isfunction(add) #是不是函数
inspect.ismethod(add) #是不是类方法
inspect.isgenerator(add) #是不是生成器
inspect.isclass(add) #是不是类
inspect.ismodule(add) #是不是模块
inspect.isbuiltin(add) #是不是内建对象
  • 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 inspect
def add(x:int,y:int,*args,**kwargs)->int:
    return x+y
sig =  inspect.signature(add)
print(sig)
print('params : ', sig.parameters)
print('return : ', sig.return_annotation)
print('~~~~~~~~~~~~~~~~')
for i,item in enumerate(sig.parameters.items()):
    name,param = item
    print(i+1,name,param.annotation,param.kind,param.default)
    print(param.default is param.empty, end = '

')

有函数如下:

def add(x,y:int=7) -> int:
    return x + y
  • 检查用户输入是否符合参数注解的要求?
  • 思路:
    • 调用时,判断用户输入的实参是否符合要求
    • 调用时,用户感觉上还是在调用add函数
    • 对用户输入的数据和声明的类型进行对比,如果不符合,提示用户
def check(fn):
    def wrapper(*args,**kwargs):
        sig = inspect.signature(fn)
        params = sig.parameters
        values = list(params.values())
        #print(values)
        for i,p in enumerate(args):
           # print(i,p)
            params = values[i]
            #print(params.annotation,params.empty)
            if params.annotation is not params.empty and not isinstance(p,params.annotation):
                print(p,'!==',values[i].annotation)
        for k,v in kwargs.items():
            if sig.parameters[k].annotation is not inspect._empty and not isinstance(v,sig.parameters[k].annotation):
                print(k,v,'!===',sig.parameters[k].annotation)
        return fn(*args,**kwargs)
    return wrapper
@check
def add(x,y:int=7) -> int:
    return x + y
#print(add(20,10))
print(add(20,y=10))
print(add(y=10,x=20))

免责声明:文章转载自《python 类型注解》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Debian下试用KDE4svg的stroke属性,神奇的描边下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python爬取网易翻译 和MD5加密

一、程序需要知识 1、python中随机数的生成 #生成 0 ~ 9 之间的随机数 #导入 random(随机数) 模块 importrandom print(random.randint(0,9)) 2、python获取当前时间和时间戳 importtime,datetime #时间戳 print(time.time()) #今天的日期 prin...

Python 读取Excel用例——openpyxl模块

openpyxl是第三方库,专门处理Excel2007及以上版本产生的xlsx文件 Python官方库一般使用xlrd库来读取Excel文件,使用xlwt库来生成Excel文件,使用xlutils库复制和修改Excel文件,这三个库只支持到Excel2003。 openpyxl中有三个不同层次的类,Workbook是对工作簿的抽象,Worksheet是对表...

用python调用caffe时出错:AttributeError: 'module' object has no attribute 'bool_'

由于用caffe的时候需要将/somepath/your_caffe/python include进来作为环境变量,但是caffe中有个io.py和numpy的io冲突,所以导致这种现象。 下面给出了一种解决方法,即对有冲突的io文件进行重命名: numpyのioとPyCaffeのio.pyが競合するようです。(Strange Issue using Py...

微信小程序----团购或秒杀的批量倒计时实现

效果图 实现思路微信小程序实现倒计时,可以将倒计时的时间进行每一秒的计算和渲染! JS模拟商品列表数据 goodsList;在 onLoad 周期函数中对活动结束时间进行提取;建立时间格式化函数 timeFormat;建立倒计时函数 countDown;在 onLoad 周期函数的提取结尾执行倒计时函数 countDown。倒计时函数详解获取当前时间,同时...

linux中常见的脚本工具

1、Perl Perl脚本最初为在Unix系统中更好的处理文本报表而设计,经过多年的发展已经成为一种广泛应用的高级脚本语言。Perl脚本对文本文件的处理功能依然非常强大,在图像处理、系统管理、网络编程、数据库编程等方面可有其用武之地。 2、Python Python是最近几年发展非常快的一种脚本编程语言,它的设计理念非常重视代码的可读性,其最显著的语法特点...

django之数据库表的单表查询

一、添加表记录 对于单表有两种方式 # 添加数据的两种方式 # 方式一:实例化对象就是一条表记录 Frank_obj = models.Student(name ="海东",course="python",birth="2000-9-9",fenshu=80) Frank_obj.save() # 方式二: mo...