下面的例子就是多态
def add(x,y):
return x+y
>>> add(1,2)
3
>>> add([1],[2])
[1, 2]
>>> add("1","2")
'12'
>>> add((),(5,))
(5,)
>>>
- python是动态的强类型的语言,而静态语言是提前检查,但是python是在运行之后才能检查是否正确
- 而输入的信息是不可控的,这也是经常出bug的地方
- 所以python引入了类型注解(python3.5以后支持)
- 既然是注解,就不是强制的类型约束
- 所以仅仅是注释性的解释,可以见下面的代码
>>> def add(x: int, y: int) ->int :
return x+y
>>> add(4, 5)
9
>>> add('2', '3')
'23'
>>> r:list = add('2', '3')
- Python3.5版本引入
- 对函数的参数进行类型注解
- 对函数的返回值进行类型注解
- 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
- 提供给第三方工具,做代码分析,发现隐藏的bug
- 函数注解的信息,保存在_annotations_属性中
- Python3.6引入。注意它也只是一种对变量的说明,非强制
- 增加文档Documentation String
- 当然,这只是一个惯例,不是强制标准,不能要求每个程序员一定为函数提供说明文档
- 函数定义更新了,文档未必更新
def add(x:int,y:int) -> int:
"""
Give two nums(int),return a new num(int)
:param x:int
:param y:int
:return: int
"""
return x+y
def ad(x,y):
return x+y
print(help(ad))
print(help(add))
结果如下
Help on function ad in module __main__:
ad(x, y)
None
Help on function add in module __main__:
add(x: int, y: int) -> int
Give two nums(int),return a new num(int)
:param x:int
:param y:int
:return: int
None
def add(x:int, y:int,z="sss") -> int:
"""
:param x:int
:param y:int
:return: int
"""
return x+y
print(add.__name__,add.__doc__,add.__annotations__,sep="\n~~~~~~~~~~\n")
结果如下
add
~~~~~~~~~~
:param x:int
:param y:int
:return: int
~~~~~~~~~~
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
annotations是依据参数类型注解返回对应的解释,并不会返回缺省值的类型
注意:类型检查是在调用时产生的
def add(x:int, y:int) -> int:
"""
:param x:int
:param y:int
:return: int
"""
if not (isinstance(x,int) and isinstance(y,int)):
return "ERROR"
return x+y
这样太罗嗦,定制化程度高,需要一个通用的
但是annotations不合适,需要inspect
signature(callable)函数,可以获取签名(函数签名包含了一个函数的信息,包括:函数名、参数类型、函数所在的类和名称空间及其他信息)
inspect模块提供了一些有用的函数帮助获取对象的信息,例如模块、类、方法、函数、回溯、帧对象以及代码对象。例如它可以帮助你检查类的内容,获取某个方法的源代码,取得并格式化某个函数的参数列表,或者获取你需要显示的回溯的详细信息。
def add(x:int, y:int) -> int:
"""
:param x:int
:param y:int
:return: int
"""
if not (isinstance(x,int) and isinstance(y,int)):
return "ERROR"
return x+y
import inspect
sig = inspect.signature(add)
print(sig.parameters)
print(sig.return_annotation)
结果如下
OrderedDict([('x', <Parameter "x: int">), ('y', <Parameter "y: int">)])
<class 'int'>
- 可以看到得到的结果是一个有序字典,因为普通的annotations是一个无序的字典
- 当然,如果没有提前写参数注解,返回值是<class 'inspect empty'>
- empty 特殊的类,用来标记default属性或者注释annotation属性的空值
- name : str(返回的就是这个形参的名字) The name of the parameter as a string.
- default : object(返回的就是这个形参的缺省值,类似__default__)
The default value for the parameter if specified. If the
parameter has no default value, this attribute is set to
Parameter.empty
. - annotation(返回的就是形参的类型)
The annotation for the parameter if specified. If the
parameter has no annotation, this attribute is set to
Parameter.empty
. - kind : str(返回的就是这个形参的传参方式)
Describes how argument values are bound to the parameter.
Possible values:
Parameter.POSITIONAL_ONLY
,Parameter.POSITIONAL_OR_KEYWORD
,Parameter.VAR_POSITIONAL
,Parameter.KEYWORD_ONLY
,Parameter.VAR_KEYWORD
.
inspect.signature(fn).parameters._kind
POSITIONAL_ONLY = _POSITIONAL_ONLY
POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD
VAR_POSITIONAL = _VAR_POSITIONAL
KEYWORD_ONLY = _KEYWORD_ONLY
VAR_KEYWORD = _VAR_KEYWORD
inspect.signature().parameters._name
def check(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
msg = "The given item is wrong!"
sig = inspect.signature(fn)
params = sig.parameters
for x,(y,z) in zip(args,params.items()):
if not isinstance(x,params[y].annotation):
raise TypeError(msg)
for k,v in kwargs.items():
if not isinstance(v,params[k].annotation):
raise TypeError(msg)
ret = fn(*args, **kwargs)
return ret
return wrapper
@check
def add(x:int,y:int) -> int:
return x+y
add(4,y='9')
结果如下
Traceback (most recent call last):
File "D:/Gitee/python/test/matrix.py", line 679, in <module>
add(4,y='9')
File "D:/Gitee/python/test/matrix.py", line 671, in wrapper
raise TypeError(msg)
TypeError: The given item is wrong!
但是这有个弊病,如果我没指定x:int,一旦我给x传入"str",那么报错就是str!=inspect.empyt, 显然这个不符合逻辑,需要优化
import functools
import inspect
from inspect import Parameter
def check(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
MSG = "Param {} type wrong, except {} but receive {}"
sig = inspect.signature(func)
params = sig.parameters
# 检查位置传参
for real_value, (arg, value) in zip(args, params.items()):
if not isinstance(real_value, value.annotation) and value.annotation != inspect._empty:
raise TypeError(MSG.format(arg, value.annotation, type(real_value)))
# 检查关键字传参
for index, value in kwargs.items():
anno = params[index].annotation
if not isinstance(value, anno) and anno != inspect._empty:
raise TypeError(MSG.format(index, anno, type(value)))
return func(*args, **kwargs)
return wrapper
@check
def add(x: int, y: int = 6, *args, m, n: int = 7, **kwargs) -> int:
pass
add(3, 5, 3, 3, 3, m=9, n="s", z=8)
结果如下
D:\software\opt\python.exe D:/pycharmProject/Way2Master4Python/scripts/check_properties.py Traceback (most recent call last): File "D:\pycharmProject\Way2Master4Python\scripts\check_properties.py", line 40, in <module> add(3, 5, 3, 3, 3, m=9, n="s", z=8) File "D:\pycharmProject\Way2Master4Python\scripts\check_properties.py", line 30, in wrapper raise TypeError(MSG.format(index, anno, type(value))) TypeError: Param n type wrong, except <class 'int'> but receive <class 'str'>