类型提示
Python3中的一个新特性
Python是一种动态语言
一个变量的类型, 是在运行时决定的
一个变量的类型在应用的生命周期是可变的
在函数中常规的定义参数类型的方法,多数是用帮助文档的形式来记录的
def add(x,y):
'''
:param x: int
:param y: int
:return: int
'''
使用这样的方式,对于我们的维护和操作是很不方便的,因此在Python3中官方就根据这个,出了一个新的特性,叫做类型提示
>>> def add(x: int, y: int) -> int: 参数x是int类型,参数y是int类型,-> 代表的是返回值,也是int类型
... return x + y
...
>>> help(add)
Help on function add in module __main__:
add(x:int, y:int) -> int
类型提示,仅用于提示数据类型,不做类型检查
>>> add('x', 'y')
'xy'
类型提示,x和y参数应该都是int类型的,但是这里可以看出,str类型也是可以使用的,
因此可以看出,类型提示就是注释的作用,不做任何类型检查
类型提示是给开发工具使用或者在运行时获取信息的,当我们使用pycharm做开发的时候,定义了类型提示,但是我们在传递值的时候,没有根据类型提示的要求来传递参数的时候,开发工具就会报出警告错误,这样可以很好的定位住问题,减少bug的出现
类型提示在3.5及3.5以前的版本的时候,只能用在参数和返回值上
在Python中是有专门记录类型提示的方法的
In [1]: def add(x: int, y: int) -> int:
...: return x + y
...:
In [2]: add.__annotations__
Out[2]: {'return': int, 'x': int, 'y': int}
Python3中 使用typing 模块和类型提示特性做类型检查
In [1]: import typing
In [2]: def sum(lst: typing.List[int]) -> int: 参数为列表类型,返回值为整型
...: ret = 0
...: for x in lst:
...: ret += x
...: return ret
...:
In [3]: sum(1) 传递一个整型的时候,报出了TypeError的错误,提示说参数是int,不是一个可迭代对象
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-5771d20eddf7> in <module>()
----> 1 sum(1)
<ipython-input-2-91941059862a> in sum(lst)
1 def sum(lst: typing.List[int]) -> int:
2 ret = 0
----> 3 for x in lst:
4 ret += x
5 return ret
TypeError: 'int' object is not iterable
In [4]: sum([1,2]) 传递一个列表,正常的执行了我们的函数
Out[4]: 3
利用类型提示实现类型检查
写一个装饰器,使用装饰器来结合Python3的新特性,审核参数的数据类型是否正确
# -*- coding: utf-8 -*-
import inspect
import functools
def Checking(fn):
@functools.wraps(fn)
def wrap(*args, **kwargs):
params = inspect.signature(fn).parameters
for k,v in kwargs.items():
param = params[k].annotation
if param.annotation != inspect._empty and not isinstance(v, param.annotation):
raise TypeError('{} 类型错误,应该是{},但是是{}'.format(k,params[k].annotation,type(v)))
for i,s in enumerate(args):
print(params.values())
param = list(params.values())[i]
if param.annotation != inspect._empty and not isinstance(s, param.annotation):
raise TypeError('{} 类型错误,应该是{},但是是{}'.format(i,param.annotation,type(s)))
return fn(*args, **kwargs)
return wrap
@Checking
def add(x: int, y:int) -> int:
return x + y
add(3,y='a')
Traceback (most recent call last):
File "E:/projects/exercise/demo3.py", line 29, in <module>
add(3,y='a')
File "E:/projects/exercise/demo3.py", line 13, in wrap
raise TypeError('{} 类型错误,应该是{},但是是{}'.format(k,params[k].annotation,type(v)))
TypeError: y 类型错误,应该是<class 'int'>,但是是<class 'str'>
functools 常用的三个方法
functools.wraps() 将被装饰函数的信息重新赋值给装饰器的二级函数,上面的例子已经用到
functools.partial(object, [param,…]) 用于固定函数中一个或者若干个参数
def add(x: int , y: int) -> int:
return x + y
new_add = functools.partial(add, y=5) 将函数的参数固定,并且生成一个新的函数
print(new_add(3)) 只需要传递x的参数值,就可以了
8
print(new_add(3, y=4))
7
使用关键字参数,是可以将被固定的参数修改的
因此可以看出,被固定的参数被修改成了一个优先级高的默认参数
函数作为参数,对这个作为参数的函数的参数列表是有限制的
functools.lru_cache
缓存数据,默认缓存为128条,可以根据自己的情况修改
根据最近最少使用(LRU)的原则来进行缓存的,超过缓存的条目后,会将最先缓存的数据,清除
缓存时间没有限制,除非解释器退出,否则永不超时
import functools
import datetime
import time
def logger(fn):
@functools.wraps(fn)
def wrap(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
end = datetime.datetime.now()
return fn
return wrap
@logger
@functools.lru_cache(128)
def sleeps(x):
time.sleep(x)
print('this is test!!')
lru的使用场景
- 不需要过期的缓存,functools.lru_cache没有过期的方法
- 不需要清除的缓存,functools.lru_cache没有清除缓存的方法,除非解释器退出
- 不需要分布式的缓存,functools.lru_cache是没有办法分布式的,只在当前解释器的内存中
- 函数必须是无副作用,获取静态的输入的
满足上面的四点就能使用functools.lru_cache