python怎样判断变量名称的合法_python 如何优雅的进行入参合法性检测?

前言

由于 python 不支持函数入参类型的限定,所以,对于 python 来说,入参合法性检测显得尤为重要。

比如,我们要实现一个整数的加法运算函数,要进行入参检测,一般会这么写,非常的不 pythonic。

def add(x, y):

if not isinstance(x, int):

raise Exception

if not isinstance(y, int):

raise Exception

return x + y

def testcases():

print(add(3, 4))

print(add(4.5, 3.2))

if __name__ == '__main__':

testcases()

要想改一下代码,其实也没优雅到哪去,半斤八两。

def add(x, y):

if not all(isinstance(item, int) for item in [x, y]):

raise Exception

return x + y

def testcases():

print(add(3, 4))

print(add(4.5, 3.2))

if __name__ == '__main__':

testcases()

铺垫

今天,为大家介绍一种 pythonic 的入参合法性校验方法。介绍这个方法之前,需要介绍一下 PEP 3107 之 Function Annotations。这个是 python3 才支持的一个特性,可以为函数的参数进行注解,如下所示。

def compile(source: "something compilable",

filename: "where the compilable thing comes from",

mode: "is this a single statement or a suite?"):

...

当然,也可以对函数的返回值进行注解,如下所示。

def haul(item: Haulable, *vargs: PackAnimal) -> "Distance":

...

这个很有用,别人看到你的源码的时候,通过注解,就可以你这个参数应该传什么进去,还有就是,当别人用 help 来显示你写的函数的 docstring 时,这些注释也会显示。

>>> def add(x: 'integer', y: 'integer') -> 'the sum':

... return x + y

...

>>> help(add)

Help on function add in module __main__:

add(x:'integer', y:'integer') -> 'the sum'

实际上,这些注释是藏在 __annotations__ 字段里的,通过如下指令可以显示。

>>> add.__annotations__

{'y': 'integer', 'x': 'integer', 'return': 'the sum'}

其实,不光是字符串,字典 __annotations__ 里的值可以是任何 object,可以是 list,可以是 tuple,甚至,可以是函数。

正文

到此为止,介绍一下更加 pythonic 的入参合法性校验方法,代码如下所示。

import functools

import inspect

def auto_type_checker(function):

@functools.wraps(function)

def wrapper(*args, **kwargs):

# fetch the argument name list.

parameters = inspect.signature(function).parameters

argument_list = list(parameters.keys())

# fetch the argument checker list.

checker_list = [parameters[argument].annotation for argument in argument_list]

# fetch the value list.

value_list = [inspect.getcallargs(function, *args, **kwargs)[argument] for argument in inspect.getfullargspec(function).args]

# initialize the result dictionary, where key is argument, value is the checker result.

result_dictionary = dict()

for argument, value, checker in zip(argument_list, value_list, checker_list):

result_dictionary[argument] = check(argument, value, checker, function)

# fetch the invalid argument list.

invalid_argument_list = [key for key in argument_list if not result_dictionary[key]]

# if there are invalid arguments, raise the error.

if len(invalid_argument_list) > 0:

raise Exception(invalid_argument_list)

# check the result.

result = function(*args, **kwargs)

checker = inspect.signature(function).return_annotation

if not check('return', result, checker, function):

raise Exception(['return'])

# return the result.

return result

return wrapper

def check(name, value, checker, function):

if isinstance(checker, (tuple, list, set)):

return True in [check(name, value, sub_checker, function) 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 testcases():

@auto_type_checker

def add(a, b, c: (int, float), d: int) -> str:

return a + b + c + d

@auto_type_checker

def test(x: lambda x: x >= 0, y: list, z: str) -> str:

return y[x] == z

class Test():

base = 1

@auto_type_checker

def add(self, a, b, c: [int, float], d: int) -> lambda x: str(x):

return self.base + a + b + c + d

try:

print(add(1, 2, 2.3, 3.4))

except Exception as e:

print(e)

try:

print(test(-2, 3, 4))

except Exception as e:

print(e)

try:

print(test(2, [1, 2, 3, 4], '3'))

except Exception as e:

print(e)

var = Test()

try:

var.add(1, 2, 3.3, 4.5)

except Exception as e:

print(e)

try:

var.add(1, 2, 3, 4)

except Exception as e:

print(e)

if __name__ == '__main__':

testcases()

其中,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:不指定 checker,如 arg2,auto_type_checker 不会为 arg2 进行合法性校验;

type 型 checker,如 arg1,auto_type_checker 会检测 arg1 是否是 int 型,如果不是,会抛出异常,而返回值必须是 list 型,否则也会抛出异常;

函数型 checker,如 arg4,auto_type_checker 会将 arg4 带入到 checker,如果 checker 的返回值是 Fasle,则抛出异常,上述代码中,arg4 只接受大于 0 的数字;

tuple/list 型 checker,如 arg3,tuple 或 list 中的所有元素都会被当作 checker,当所有 checker 都无法通过校验,则抛出异常,上述代码中,arg3 允许整数或浮点数。

这样声明函数,一是利用 PEP 3107 之 Function Annotations 特性为函数入参出参添加注释,调用者可以知道应该往函数里传入什么变量,另外只需要添加一行 @auto_type_checker 便可以实现入参出参的自动检测,非常的 pythonic。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值