这里是一段防爬虫文本,请读者忽略。
本文原创首发于CSDN,作者IDYS
博客首页:https://blog.csdn.net/weixin_41633902/
本文链接:https://blog.csdn.net/weixin_41633902/article/details/108007811
未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!
写在开头的话
- 请记住:实践是掌握知识的最快方法
- 如果你只是怀着看看的态度去快速浏览文章,而不去认认真真的把文章里面讲的任何一个知识点去实践一遍,那么你永远也掌握不了它
- 生命不息,折腾不止!
Python类型注解
00. 函数定义的弊端
Python
是动态语言,变量随时可以被赋值,且能赋值为不同的类型Python
不是静态编译型语言,变量类型是在运行器决定的- 动态语言很灵活,但是这种特性也是弊端
def add(x, y):
return x + y
print(add(4, 5))
print(add('hello', 'world'))
add(4, 'hello')
- 难发现:由于不做任何类型检查,直到运行期问题才显现出来,或者线上运行时才能暴露出问题
- 难使用:函数的使用者看到函数的时候,并不知道你的函数的设计,并不知道应该传入什么类 型的数据
01. 函数注解Function Annotations
- 如何解决这种动态语言定义的弊端呢?
- 增加文档
Documentation String
这只是一个惯例,不是强制标准,不能要求程序员一定为函数提供说明文档 - 函数定义更新了,文档未必同步更新
def add(x, y):
'''
:param x:int
:param y: int
:return: int
'''
return x + y
print(help(add))
- 函数注解
def add(x:int , y:int) -> int :
'''
:param x: int
:param y: int
:return: int
'''
return x + y
print(help(add))
print(add(4, 5))
print(add('mag', 'edu'))
- 函数注解
Python 3.5
引入- 对函数的参数进行类型注解
- 对函数的返回值进行类型注解
- 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
- 提供给第三方工具,做代码分析,发现隐藏的
bug
- 函数注解的信息,保存在
__annotations__
属性中
def add(x:int, y:int) -> int:
return x + y
if __name__ == "__main__":
print(add.__annotations__)
- 运行结果
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
- 变量注解
Python 3.6
引入i : int = 3
02. 业务应用
- 函数参数类型检查
- 思路
- 函数参数的检查,一定是在函数外
- 函数应该作为参数,传入到检查函数中
- 检查函数拿到函数传入的实际参数,与形参声明对比
__annotations__
属性是一个字典,其中包括返回值类型的声明。假设要做位置参数的判断,无法和字典中的声明对应。使用inspect
模块
inspet
模块- 提供获取对象信息的函数,可以检查函数和类、类型检查
03. inspect模块
-
signature(callable)
,获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息) -
代码演示
import inspect
def add(x: int, y: int, *args, **kwargs) -> int:
return x + y
if __name__ == "__main__":
sig = inspect.signature(add)
print(sig, type(sig)) # 函数签名
print('params : ', sig.parameters) # OrderedDict
print('return : ', sig.return_annotation)
print(sig.parameters['y'], type(sig.parameters['y']))
print(sig.parameters['x'].annotation)
print(sig.parameters['args'])
print(sig.parameters['args'].annotation)
print(sig.parameters['kwargs'])
print(sig.parameters['kwargs'].annotation)
- 运行结果
(x: int, y: int, *args, **kwargs) -> int <class 'inspect.Signature'>
params : OrderedDict([('x', <Parameter "x: int">), ('y', <Parameter "y: int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
return : <class 'int'>
y: int <class 'inspect.Parameter'>
<class 'int'>
*args
<class 'inspect._empty'>
**kwargs
<class 'inspect._empty'>
inspect.isfunction(add)
,是否是函数inspect.ismethod(add))
,是否是类的方法inspect.isgenerator(add))
,是否是生成器对象inspect.isgeneratorfunction(add))
,是否是生成器函数inspect.isclass(add))
,是否是类inspect.ismodule(inspect))
,是否是模块inspect.isbuiltin(print))
,是否是内建对象- 还有很多
is
函数,需要的时候查阅 inspect
模块帮助
- 演示
import inspect
import parameter
def add(x: int, y:int) -> int:
return x + y
def my_gen():
yield 1
yield 2
yield 3
def inspect_perform():
print(inspect.isfunction(add))
print(inspect.ismethod(add))
print(inspect.isgenerator((x for x in range(10))))
print(inspect.isgenerator([x for x in range(10)]))
print(inspect.isgeneratorfunction((x for x in range(10))))
print(inspect.isclass(add))
print(inspect.ismodule(parameter))
print(inspect.ismodule(inspect))
print(inspect.isbuiltin(print))
print(inspect.isgeneratorfunction(my_gen))
if __name__ == "__main__":
inspect_perform()
- 运行结果
True
False
True
False
False
False
True
True
True
True
- Parameter对象
- 保存在元组中,是只读的
name
,参数的名字annotation
,参数的注解,可能没有定义default
,参数的缺省值,可能没有定义empty
,特殊的类,用来标记default
属性或者注释annotation
属性的空值kind
,实参如何绑定到形参,就是形参的类型POSITIONAL_ONLY
,值必须是位置参数提供 # 未实现POSITIONAL_OR_KEYWORD
,值可以作为关键字或者位置参数提供VAR_POSITIONAL
,可变位置参数,对应*args
KEYWORD_ONLY
,keyword-only
参数,对应*
或者*args
之后的出现的非可变关键字参数VAR_KEYWORD
,可变关键字参数,对应**kwargs
- 代码演示
import inspect
def type_perform(x, y: int, z: int = 2, *args, m, k=5, **kwargs) -> int:
return x + y
sig = inspect.signature(type_perform)
print(sig)
print(sig.parameters)
print(sig.return_annotation)
print(type_perform.__annotations__)
for i, item in enumerate(sig.parameters.items()):
par, my_type = item
print(i+1, par, my_type.annotation, my_type.default, my_type.kind)
print(my_type.default is my_type.empty, end="\n")
print(my_type.annotation is my_type.empty, end="\n\n")
x = 1
print(sig.parameters["y"].annotation is type(x))
print(type(x) is int)
- 运行结果
(x, y: int, z: int = 2, *args, m, k=5, **kwargs) -> int
OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y: int">), ('z', <Parameter "z: int = 2">), ('args', <Parameter "*args">), ('m', <Parameter "m">), ('k', <Parameter "k=5">), ('kwargs', <Parameter "**kwargs">)])
<class 'int'>
{'y': <class 'int'>, 'z': <class 'int'>, 'return': <class 'int'>}
1 x <class 'inspect._empty'> <class 'inspect._empty'> POSITIONAL_OR_KEYWORD
True
True
2 y <class 'int'> <class 'inspect._empty'> POSITIONAL_OR_KEYWORD
True
False
3 z <class 'int'> 2 POSITIONAL_OR_KEYWORD
False
False
4 args <class 'inspect._empty'> <class 'inspect._empty'> VAR_POSITIONAL
True
True
5 m <class 'inspect._empty'> <class 'inspect._empty'> KEYWORD_ONLY
True
True
6 k <class 'inspect._empty'> 5 KEYWORD_ONLY
False
True
7 kwargs <class 'inspect._empty'> <class 'inspect._empty'> VAR_KEYWORD
True
True
True
True
04. 业务功能
- 有函数如下
def add(x, y:int=7) -> int:
return x + y
-
请检查用户输入是否符合参数注解的要求?
-
思路
- 调用时,判断用户输入的实参是否符合要求
- 调用时,用户感觉上还是在调用
add
函数 - 对用户输入的数据和声明的类型进行对比,如果不符合,提示用户
- 代码演示
import functools
import inspect
def logger(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
sig_parameter = sig.parameters
para_tup = tuple(sig_parameter.keys())
for i, v in enumerate(args): # 对
key = para_tup[i]
if isinstance(v, sig_parameter[key].annotation):
print("{}{}{}".format(v, "type is ", sig_parameter[key].annotation))
else:
error = "{}\t{}{}".format(v, "type is not ", sig_parameter[key].annotation)
print(error)
raise TypeError(error)
if len(args) + len(kwargs) > len(sig_parameter): # 判断用户输入的参数是否多了,如果输入的参数多了,那么就调整使其不报错。仍然能够运算
new_backup = list(args)
new_backup.extend(kwargs.values())
args = tuple(new_backup[:2])
kwargs.clear()
for kw_key, kw_value in kwargs.items(): # 对kwargs的类型进行判断
if isinstance(kw_value, sig_parameter[kw_key].annotation):
print("{}{}{}".format(kw_value, "type is ", sig_parameter[kw_key].annotation))
else:
error = "{}{}{}".format(kw_value, "type is not", sig_parameter[kw_key].annotation)
print(error)
raise TypeError(error)
ret = fn(*args, **kwargs)
return ret
return wrapper
@logger
def add(x: int, y: int = 8) -> int:
"""
:param x: int
:param y: int
:return: int
"""
return x + y
if __name__ == "__main__":
print(add(3, y=3, z=10, h=30))
print(add(34, y=15))
print(add(3, 5))
print(add(2, 5.0))
- 运行结果
3 type is <class 'int'>
6
34 type is <class 'int'>
15 type is <class 'int'>
49
3 type is <class 'int'>
5 type is <class 'int'>
8
2 type is <class 'int'>
5.0 type is not <class 'int'>
Traceback (most recent call last):
File "E:/Users/dayuanshuai/PycharmProjects/douban/parameter/demo06.py", line 56, in <module>
print(add(2, 5.0))
File "E:/Users/dayuanshuai/PycharmProjects/douban/parameter/demo06.py", line 23, in wrapper
raise TypeError(error)
TypeError: 5.0 type is not <class 'int'>
写在最后的话:
- 无论每个知识点的难易程度如何,我都会尽力将它描绘得足够细致
- 欢迎关注我的CSDN博客,IDYS’BLOG
- 持续更新内容:
linux基础 | 数据通信(路由交换,WLAN) | Python基础 | 云计算 - 如果你有什么疑问,或者是难题。欢迎评论或者私信我。你若留言,我必回复!
- 虽然我现在还很渺小,但我会做好每一篇内容。谢谢关注!