由于 python 不支援函數入參類型的限定,是以,對于 python 來說,入參合法性檢測顯得尤為重要。
def add(a: int, b: int) -> int:
return a + b
if __name__ == '__main__':
print(add(1, "2"))
-------------------------------------------------------------
Traceback (most recent call last):
File "D:/MyPython/checker/checker_param.py", line 9, in <module>
print(add(1, "2"))
File "D:/MyPython/checker/checker_param.py", line 5, in add
return a + b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
python inspect
需要介紹一下 PEP 3107 之 Function Annotations。這個是 python3 才支援的一個特性,可以為函數的參數進行注解,如下所示。
上文的add函數的int也可以寫成:
def add(x: 'integer', y: 'integer') -> 'the sum':
return x + y
别人看到你的源碼的時候,通過注解,就可以你這個參數應該傳什麼進去,實際上,這些注釋是藏在 __annotations__ 字段裡的,通過如下指令可以顯示。
print(add.__annotations__)
{'x': 'integer', 'y': 'integer', 'return': 'the sum'}
字典 __annotations__ 裡的值可以是任何 object,可以是 list、tuple、函數等。
直接上代碼:
# coding=utf-8
import functools
import inspect
def check(name, value, checker):
if isinstance(checker, (tuple, list, set)):
return True in [check(name, value, sub_checker) for sub_checker in checker]
elif checker is inspect._empty:
return True
elif checker is None:
return value is None
elif isinstance(checker, type):
return isinstance(value, checker)
elif callable(checker):
result = checker(value)
return result
def auto_type_checker(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
sig = inspect.signature(function)
parameters = sig.parameters
# fetch the argument name list.
argument_list = list(parameters.keys())
# fetch the parameter type list.
type_list = [parameters[argument].annotation for argument in argument_list]
# fetch the parameter value list.
value_list = sig.bind(*args, **kwargs).arguments.values()
# check the invalid argument, and raise the error.
for argument, value, checker in zip(argument_list, value_list, type_list):
if not check(argument, value, checker):
raise Exception(f"arg '{argument}' required type:{checker},got type {type(value)}")
# check the result.
result = function(*args, **kwargs)
checker = sig.return_annotation
if not check('return', result, checker):
raise Exception(['return'])
# return the result.
return result
return wrapper
@auto_type_checker
def add(a: int, b: int) -> int:
return a + b
if __name__ == '__main__':
print(add.__annotations__)
print(add(1, 4))
其中,auto_type_checker 是一個修飾器,在函數定義的時候調用即可。函數在聲明的時候,如果需要進行入參合法性校驗的話,就用如下文法為函數的輸入輸出指定 checker。
@auto_type_checker
def function(arg1: int, arg2, arg3: (int, float) = 0, arg4: lambda x: x > 0 = 1) -> list:
return [arg1, arg2, arg3, arg4]
上述代碼有 4 種 checker:
- arg1 <class 'int'>:type 型 checker,如 arg1,auto_type_checker 會檢測 arg1 是否是 int 型,如果不是,會抛出異常,而傳回值必須是 list 型,否則也會抛出異常;
- arg2 <class 'inspect._empty'>:不指定 checker,如 arg2,auto_type_checker 不會為 arg2 進行合法性校驗;
- arg3 (<class 'int'>, <class 'float'>):tuple/list 型 checker,如 arg3,tuple 或 list 中的所有元素都會被當作 checker,當所有 checker 都無法通過校驗,則抛出異常,上述代碼中,arg3 允許整數或浮點數,0為預設值。
- arg4 <function <lambda> at 0x0000020126AD8730>:函數型 checker,如 arg4,auto_type_checker 會将 arg4 帶入到 checker,如果 checker 的傳回值是 Fasle,則抛出異常,上述代碼中,arg4 隻接受大于 0 的數字,1為預設值。;
測試:
print(function(1, 2, 3, 4))
print(function(1, 2, 3.0, 4))
print(function(1, 2, 3, -4))
************************************
[1, 2, 3, 4]
[1, 2, 3.0, 4]
print(function(1, 2, 3, -4))
File "D:/MyPython/checker/checker_param.py", line 39, in wrapper
raise Exception(f"arg '{argument}' required type:{checker},got type {type(value)}")
Exception: arg 'arg4' required type:<function <lambda> at 0x0000026EC42C8730>,got type <class 'int'>
參考:
- https://zhuanlan.zhihu.com/p/49078420
- https://juejin.cn/post/6844903849963028487
- https://vimsky.com/examples/detail/python-method-inspect.signature.html