def add(x, y):
return x + y
print(add(4, 5))
print(add('hello', 'world'))
# add(4, 'hello') 报错
运行结果
9
helloworld
如何解决这种动态语言定义的弊端呢 ?
def add(x, y):
"""
:param x: int
:param y: int
:return: int
"""
return x + y
add.__annotations__
栗子 :
def add(x:int, y:int) -> int:
"""
:param x: int
:param y: int
:return: int
"""
return x + y
print(add(4, 5))
print(add('hello', 'world'))
Python 3.6引入, 注意他只是一种对变量的说明, 非强制
函数参数类型检查
思路 :
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print(1, sig, type(sig))
print(2, sig.parameters)
print(3, sig.return_annotation)
print(4, sig.parameters['y'], sig.parameters['y'].annotation, type(sig.parameters['y']))
print(5, sig.parameters['args'], sig.parameters['args'].annotation)
print(6, sig.parameters['kwargs'], sig.parameters['kwargs'].annotation)
运行结果
1 (x:int, y:int, *args, **kwargs) -> int <class 'inspect.Signature'>
2 OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
3 <class 'int'>
4 y:int <class 'int'> <class 'inspect.Parameter'>
5 *args <class 'inspect._empty'>
6 **kwargs <class 'inspect._empty'>
保存在元祖中, 是只读的
name, 参数的名字
annotation, 参数的注解, 可能是没有定义
default, 参数的缺省值, 可能没有定义
empty, 特殊的类, 用来标记default 属性或者注释annotation 属性的空值
kind, 实参如何绑定到实参, 就是形参的类型
栗子
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print(sig.parameters)
print(sig.return_annotation)
for i, item in enumerate(sig.parameters.items()):
name, param = item
print(i, name, param, param.annotation, param.kind, param.default)
运行结果
OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
<class 'int'>
0 x x:int <class 'int'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>
1 y y:int <class 'int'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>
2 args *args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'>
3 kwargs **kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'>
有如下函数, 如何对参数进行验证是否符合要求呢 ?
def add(x, y:int=7) -> int:
return x + y
分析
import inspect
def add(x, y:int) -> int:
return x + y
def check(fn):
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
params = sig.parameters
values = list(params.values())
for i, p in enumerate(args):
if isinstance(p, values[i].annotation):
print('OK')
for k, v in kwargs.items():
if isinstance(v, params[k].annotation):
print('OK')
return fn(*args, **kwargs)
return wrapper
# 调用测试
test1 = check(add)(20, 10)
test2 = check(add)(20, y=10)
test3 = check(add)(y=10, x=20)
print(test1, test2, test3)
运行结果
OK
OK
OK
30 30 30
这里简单实现了参数校验, 但是业务需求是参数有注解要求实参类型和声明类型一致, 没有注解的情况下如何修改代码呢 ?
import inspect
def check(fn):
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
params = sig.parameters
values = list(params.values())
for i, p in enumerate(args):
param = values[i]
if param.annotation is not param.empty and not isinstance(p, param.annotation):
print(p, '!==', values[i].annotation)
for k, v in kwargs.items():
if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):
print(k, v, '!===', params[k].annotation)
return fn(*args, **kwargs)
return wrapper
@check
def add(x, y:int) -> int:
return x + y
# 调用测试
test1 = add('a', 'b')
test2 = add('c', y='d')
test3 = add(y=10, x=20)
print(test1, test2, test3)
运行结果
b !== <class 'int'>
y d !=== <class 'int'>
ab cd 30