1. 主页
  2. Python基础到高级
  3. 类型提示

类型提示

类型提示

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

我们要如何帮助您?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注