函数参数注解

函数定义的弊端
  • Python是动态语言,变量随时可以被赋值,且能赋值为不同的类型
  • Python不是静态编译型语言,变量类型是在运行期决定的
  • 动态语言很灵活,但是这种特性也是弊端
    def add(x, y):
        return x + y
    print(add(4, 5))
    print(add('hello', 'world'))
    add(4, 'hello') 
    
  • 难发现:由于不做任何类型检查,直到运行期问题才显现出来,或者线上运行时才能暴露出问题
  • 难使用:函数的使用者看到函数的时候,并不知道你的函数的设计,并不知道应该传入什么类型的数据
  • 如何解决这种动态语言定义的弊端呢?
    • 增加文档Documentation String
    • 这只是一个惯例,不是强制标准,不能要求程序员一定为函数提供说明文档
    • 函数定义更新了,文档未必同步更新
def add(x, y):
''':param x: int:param y: int:return: int'''
return x + y
print(help(add))
函数注解Function Annotations
  • 如果解决这种动态语言定义的弊端呢?
    • 函数注解
    def add(x , y:int) -> int :  
        return x + y 
    print(add.__annotations__)
    sig =inspect.signature(add)
    print(sig)  
    --------------------------------------------
    {'y': <class 'int'>, 'return': <class 'int'>}   #函数中有注解的则显示,无注解的不显示,可变参数不显示
    (x, y: int) -> int   # 函数签名依然是所有的参数
    

函数注解Function Annotations :

  • 对函数的参数进行类型注解
  • 对函数的返回值进行类型注解
  • 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
  • 提供给第三方工具,做代码分析,发现隐藏的bug
  • 函数注解的信息,保存在 __annotations__属性中
  • 变量注解
    • Python 3.6引入。注意它也只是一种对变量的说明,非强制
      例如 i : int = 3
业务应用
  • 函数参数类型检查
  • 思路
    • 函数参数的检查,一定是在函数外,不要把检查代码侵入到函数中
    • 函数应该作为参数,传入到检查函数中
    • 检查函数拿到函数传入的实际参数,与形参声明对比
    • __ annotations__属性是一个字典,其中包括返回值类型的声明。假设要做位置参数的判断,无法和字典中的声明对应。使用inspect模块
  • inspet模块
    • 提供获取对象信息的函数,可以检查函数和类、类型检查

inspect 模块
  1. signature(callable),获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息)
import inspect
def fn(x:int,y:int,z:int,*args,**kwargs) -> int:
    return x + y + z
sig = inspect.signature(fn)  #签名函数

print(sig)
print(type(sig))
print("params:",sig.parameters)  #相当于有序字典
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)
-------------------------------------------------------------
(x:int, y:int, z:int, *args, **kwargs)
<class 'inspect.Signature'>
params: OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('z', <Parameter "z:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
return, <class 'inspect._empty'>
y:int <class 'inspect.Parameter'>
<class 'int'>
*args
<class 'inspect._empty'>
**kwargs
<class 'inspect._empty'>

2 inspect模块 is 做判断

import inspect
def fn(x:int,y:int,z:int,*args,**kwargs) -> int:
    return x + y + z
    
- inspect.isfunction(fn) #是否是函数
>>True
- inspect.isgeneratorfunction(fn)  #判断是否是函数生成器
>>False
- inspect.isgneerator(fn)  # 判断是否是生成器
>>False
- inspect.isbuiltin(print))#是否是内建对象
>>False
  1. Parameter(参数)对象
  • 保存在元组中,是只读的
  • name,参数的名字
  • annotation,参数的注解,可能没有定义
  • default,参数的缺省值,可能没有定义
  • empty,特殊的类,用来标记default属性或者注释annotation属性的空值
  • kind,实参如何绑定到形参,就是形参的类型
    • POSITIONAL_ONLY,值必须是位置参数提供
    • POSITIONAL_OR_KEYWORD,值可以作为关键字或者位置参数提供
    • VAR_POSITIONAL,可变位置参数,对应*args
    • KEYWORD_ONLY,keyword-only参数,对应或者args之后的出现的非可变关键字参数
    • VAR_KEYWORD,可变关键字参数,对应**kwargs
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='\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 functools,inspect
def fn(x:int,y:int = 2,*args,**kwargs) -> int: 
    return x + y

fn.__annotations__           >>>  {'x': int, 'y': int, 'return': int}  #获取函数注解,包括参数和返回值
sig = inspect.signature(fn)  >>>  <Signature (x: int, y: int = 2, *args, **kwargs) -> int>  # 获取签名  主要是参
params = sig.parameters      >>>  mappingproxy({'x': <Parameter "x: int">,'y': <Parameter "y: int = 2">,'args': <Parameter "*args">,'kwargs': <Parameter "**kwargs">})  # 获取参数类型的有序字典 
params['x'].annotation       >>> int,用于参数类型判断,与传入的实参进行比较

例如:

import inspect
def add(x,y:int) -> int:
    return x + y 
    
sig = inspect.signature(add)
params = sig.parameters
print(params)
params
-------------------------
OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y:int">)])
mappingproxy({'x': <Parameter "x">, 'y': <Parameter "y:int">}) 

1111111.png

业务应用

有函数如下

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

请检查用户输入是否符合参数注解的要求?
思路
调用时,判断用户输入的实参是否符合要求
调用时,用户感觉上还是在调用add函数
对用户输入的数据和声明的类型进行对比,如果不符合,提示用户

import inspect

def add(x, y, z):
    return x + y + z

def check(fn):
    def wrapper(*args, **kwargs):
        sig = inspect.signature(fn)
        params = sig.parameters       #得到的是有序字典
        print(sig)
        print(params)
        va = list(params.values())
        # keys = list(params.keys())
        for i, v in enumerate(args):
            if va[i].annotation != inspect._empty and not isinstance(v, va[i].annotation):
                print("1输入错误")
        for k,v,in kwargs.items():
            if params[k].annotation != inspect._empty and not isinstance(v,params[k].annotation):
                print("2输入错误")
        cc = fn(*args, **kwargs)
        return cc
    return wrapper

@check
def add(x, y:int,z:int) ->int:
    return x + y + z
print(add(1,2,10))
--------------------------------------------
(x, y:int, z:int) -> int
OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y:int">), ('z', <Parameter "z:int">)])
13

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值