注释 函数注释 【示例】:
1 2 3 4 5 6 7 8 9 10 11 12 @staticmethod def report_info_add (report_a: dict , report_b: dict , key: str ) -> int : """ 报告信息相加 :param report_a: dict 报告信息字典 A :param report_b: dict 报告信息字典 B :param key: str 指定 key 值 :return: int 相加后的数值 """ report_a_value = report_a[key] if key in report_a else 0 report_b_value = report_b[key] if key in report_b else 0 return report_a_value + report_b_value
在函数声明中 report_a 是参数,: 冒号后 dict 是参数 report_a 的注释,表示参数的数据类型。如果参数有默认值,则可以写成:
函数声明头部最后的箭头,则是函数返回值的注释,表明函数返回的数据类型,也可以是其他内容。
这些注释信息都是函数的元信息,保存在 f.annotations 字典中。
【注意】:
元信息(注释信息)的值可以是:
object,例如上述示例中的 dict、int,也可以是具体的对象 MyObject;
str:通过字符串可以提供更丰富的注释信息,例如 `def sum(a, b) -> “the sum of a and b”:
Python 对元信息(注释信息)和 f.annotations 的一致性不做检查。换言之,元信息仅仅只是帮助开发人员理解函数。
奇妙用法 无论是编写插件还是开发项目,我们都需要保证程序的健壮性,在这之中,对输入数据类型的检测为重要。但是,对每个函数都进行输入数据的类型检测是一份非常辛苦的工作,尤其当项目非常庞大时。有没有什么好的办法能够自动对输入数据的类型进行检测呢?上面所讲的函数注释的元信息可以帮助我们达成这个目标。
首先,定义输入数据类型检测函数。
1 2 3 4 def input_check (annotations: dict , **kwargs ) -> None : for param_name in kwargs.keys(): if type (kwargs[param_name]) != annotations[param_name]: raise TypeError("The param {} must be {}!" .format (param_name, annotations[param_name]))
在其他函数的开始位置调用 input_check 函数。
1 2 3 def read_data (path: str , file_type: str = "txt" ): input_check(read_data.__annotations__, path=path, file_type=file_type)
【示例】:输入类型不匹配。
1 2 3 4 if __name__ == '__main__' : read_data("D://code/name.json" , [])
当然,我们也可以对其进行错误捕获与处理。
1 2 3 4 5 6 7 def read_data (path: str , file_type: str = "txt" ): try : input_check(read_data.__annotations__, path=path, file_type=file_type) except TypeError as error: print (error)
每次在函数开始位置都要调用 input_check() 仍然是一件“麻烦”的事情,有没有更简单易用的方式?毕竟偷懒是推动技术发展的重要动力嘛。当然有,我们可以通过装饰函数来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def input_check (*args, **kwargs ): def decorate (func ): annotations = func.__annotations__ sig = signature(func) @wraps(func ) def wrapper (*args, **kwargs ): param_dict = sig.bind(*args, **kwargs).arguments for param_name, param_value in param_dict.items(): if type (param_value) != annotations[param_name]: raise TypeError("The param {} must be {}!" .format (param_name, annotations[param_name])) return func(*args, **kwargs) return wrapper return decorate @input_check() def read_data (path: str , file_type: str = "txt" ): if __name__ == '__main__' : read_data("D://code/name.json" , [])
除了通过函数注释的元信息来强制规定参数类型外,我们也可以通过给装饰器函数传递参数的形式来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def input_check (*args, **kwargs ): def decorate (func ): sig = signature(func) param_type_dict = sig.bind(*args, **kwargs).arguments @wraps(func ) def wrapper (*args, **kwargs ): param_values = sig.bind(*args, **kwargs).arguments for param_name, param_value in param_values.items(): if type (param_value) != param_type_dict[param_name]: raise TypeError("The param {} must be {}!" .format (param_name, param_type_dict[param_name])) return func(*args, **kwargs) return wrapper return decorate @input_check(str , str ) def read_data (path: str , file_type: str = "txt" ):
2024-04-09 更新:GPT-4 编写的使用类型注释和装饰器进行参数校验的代码。这种方式特别适用于需要强制类型检查的情况。下面是一个例子,使用类型注解和一个自定义装饰器来检查函数参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from functools import wrapsfrom typing import Any , Callable def type_check (func: Callable ) -> Callable : @wraps(func ) def wrapper (*args, **kwargs ): func_annotations = func.__annotations__ all_args = kwargs.copy() all_args.update(dict (zip (func.__code__.co_varnames, args))) for arg_name, arg_value in all_args.items(): if arg_name in func_annotations: expected_type = func_annotations[arg_name] if not isinstance (arg_value, expected_type): raise TypeError(f"Argument '{arg_name} ' must be {expected_type} " ) return func(*args, **kwargs) return wrapper @type_check def my_function (param1: int , param2: float ) -> float : return param1 + param2 try : result = my_function(5 , 3.14 ) print (result) result = my_function('5' , 3.14 ) except TypeError as e: print (f"Error: {e} " )
在这个例子中,type_check
装饰器会检查 my_function
的参数类型是否与注解相符。如果传递了错误的类型,将会抛出 TypeError
。
这种方法的优点是类型注解本身就作为文档,说明了函数应该如何被调用。同时,装饰器在运行时强制这些类型规则,确保了类型安全。请注意,Python的类型注解本身不会强制类型检查,它们通常用于文档目的或者使用第三方工具如 mypy 进行静态类型检查。我们在这里通过自定义装饰器在运行时实施类型检查。