Python高级 —— 弥补动态类型的短板(函数注解、变量注解、inspect)

 
 
 
 
 
 
 
 

函数注解

用途:弥补python的动态语言存在的弊端,即某变量无法为其直接声明类型,在代码编写及测试阶段没有出现异常,但是当项目上线后由于数据类型导致程序出错。 如编写一个求和的函数,但是没有在函数内对输入数据的类型进行完全的判断,导致用户输入数值与字符串从而报错的情况

 

注意:该功能不提供强制性的限制功能,只提供一个辅助说明功能。常用于提供给第三方工具,做代码分析,发现隐藏Bug

 

适用: python 3.5以后

 

实现形式:该功能将注解存储在函数的 .__annotations__属性中
在这里插入图片描述
在这里插入图片描述

 

错误的情况:
在这里插入图片描述

 

更好的做法:
在这里插入图片描述

 
 
 
 

变量注解

形式: i:int = 5

 

注意:该功能不提供强制性的限制功能,只提供一个辅助说明功能。常用于提供给第三方工具,做代码分析,发现隐藏Bug

 

适用: python 3.6以后

 

实现形式:该功能将注解存储在函数的 .__annotations__属性中,但此属性对应的数据类型为字典(普通的无序字典)

 
 
 
 

应用场景

常用于业务中对函数及其参数类型进行检测,以弥补 Python 动态类型带来的一些弊端

 
 

借助 inspect 模块

功能:用于检测函数的函数名、参数、缺省值、函数定义等相关信息,对非函数的可调用对象均有效

 

主要方法:

import inspect
inspect. signature(func)

 

如:

 

查询函数签名

import inspect

def add(x:int, y:int, *args, **kwargs) -> int:
    return x + y


sig = inspect.signature(add)
print('sig is : ', sig)
print('sig type is : ', type(sig))

在这里插入图片描述

 
 

查询函数参数的有序字典

import inspect

def add(x:int, y:int, *args, **kwargs) -> int:
    return x + y


sig = inspect.signature(add)
print('\n')
print(sig.parameters)
print(type(sig.parameters))
print('\n')

在这里插入图片描述

 
 

parameter对象

parameters是一个有序的字典,parameter是字典中每个参数的类型,可通过parameter对象获取如下信息:

属性含义
parameterObj.name参数名
parameterObj.annotation参数注解
parameterObj.default参数缺省值
parameterObj.kind形参类型
parameterObj.empty该参数无函数注解的class

parameterObj.empty 是非常重要的一个属性,会返回一个type。后续要用到这个属性来判断某个参数是否设置了注解,从而判断传入的实参与形参是否对应。若缺失了该属性,则难以完成该功能。

print(para.empty)
print(type(para.empty))

在这里插入图片描述

 
 

查询函数返回值

import inspect

def add(x:int, y:int, *args, **kwargs) -> int:
    return x + y


sig = inspect.signature(add)
print('\n')
print(sig.return_annotation)
print('\n')
print(type(sig.return_annotation))
print('\n')

在这里插入图片描述

 
 

查询函数某个参数及其返回值类型

import inspect

def add(x:int, y:int, *args, **kwargs) -> int:
    return x + y


sig = inspect.signature(add)
print('\n')
print(sig.parameters['x'])
print('\n')
print(type(sig.parameters['x']))
print('\n')

在这里插入图片描述

 
 

查询函数某个参数的返回值类型

import inspect

def add(x:int, y:int, *args, **kwargs) -> int:
    return x + y


sig = inspect.signature(add)
print('\n')
print(sig.parameters['y'].annotation)
print('\n')
print(type(sig.parameters['y'].annotation))
print('\n')

在这里插入图片描述

 
 

收集参数无可查询的类型

import inspect

def add(x:int, y:int, *args, **kwargs) -> int:
    return x + y


sig = inspect.signature(add)
print('\n')
print(sig.parameters['args'])
print('\n')
print(sig.parameters['args'].annotation)
print('\n')

在这里插入图片描述

 
 

inspect.isxxx 判断对象类型

函数说明
ispect.isfunction(add)是否为函数
inspect.ismethod(add)是否为类的方法
inspect.isgenerator(add)是否为生成器对象
inspect.isgeneratorfunction(add)是否为生成器函数
inspect.isclass(add)是否为类
inspect.ismodule(add)是否为模块
inspect.isbuiltin(add)是否为内建模块

 
 
 
 

业务场景

检测函数传入的实参是否符合形参的注解要求

import inspect
import functools


class ParameterTypeNotAvailable(Exception):
    pass


def check(func):
    @functools.wraps(func)
    def _wrapper(*args, **kwargs):
        # get the signature from the the function --- func
        sig = inspect.signature(func)

        # get parameters' object ordered dictionary
        params = sig.parameters

        # check whether the parameters putted into the func is same
        # as the sign of function's annotation
        # depend on the order
        paraObjList = tuple(params.values())
        for i, eachPara in enumerate(args):
            # make sure the parameter annotation is entered
            if not (paraObjList[i].annotation == paraObjList[i].empty):
                if not isinstance(eachPara, paraObjList[i].annotation):
                    raise ParameterTypeNotAvailable

        # kwargs
        for kwKey, kwValue in kwargs.items():
            # make sure the parameter annotation is entered
            if not (params[kwKey].annotation == params[kwKey].empty):
                if not isinstance(kwValue, params[kwKey].annotation):
                    raise ParameterTypeNotAvailable

        res = func(*args, **kwargs)
        return res

    return _wrapper

@check
def add(x: int, y) -> int:
    return x + y

print(add(1, 2))

add()函数注解了x为int类型,y未定义具体类型

当传入的x、y均为整数的时候,程序正常运行,结果如下
在这里插入图片描述

 

但当传入的x变为1.0的时候,程序会抛出异常,结果如下:
在这里插入图片描述

 
 
 
 
 
 
 
 

其他用法


 

获取源码

借助 inspect.getsource(func)
 
如:

print(inspect.getsource(logit_test))

在这里插入图片描述
 
通过命令行调用该功能
python -m inspect logging
python -m inspect requests:Request
 
在这里插入图片描述

 
 
 
 

获取 doc

借助 inspect.getdoc(func)
 

inspect.getdoc(logit_test)

在这里插入图片描述

 
 
 
 

获取文件所在的位置

借助 inspect.getfile(func)

 
 
 
 

获取生成器当前状态

import inspect


def get(n):
    for i in range(n):
        yield i


gen = get(2)
print(inspect.getgeneratorstate(gen))
print(next(gen))
print(inspect.getgeneratorstate(gen))
for i in gen:
    print(i)
print(inspect.getgeneratorstate(gen))

在这里插入图片描述

 
 
 
 

获取生成器当前返回的值

inspect.getgeneratorlocals

 

import inspect


def get(n):
    for i in range(n):
        yield i


gen = get(2)
print(inspect.getgeneratorlocals(gen))
next(gen)
print(inspect.getgeneratorlocals(gen))
next(gen)
print(inspect.getgeneratorlocals(gen))

在这里插入图片描述

posted @ 2022-06-05 20:49  阿伦alun  阅读(143)  评论(0编辑  收藏  举报