python基础练习-装饰器的应用
装饰器的应用
一、实现一个cache装饰器,实现可过期被清除的功能
简化设计,函数的形参定义不包含可变位置参数、可变关键字参数和keyword-only参数,如def add(x=[],y=5)
,这样会提示list是不能hash的,可变类型不可作为参数。可以不考虑缓存满了之后的换出问题。
数据类型的选择
缓存的应用场景,是有数据需要频繁查询,且每次查询都需要大量计算或者等待时间之后才能返回结果的情况,使用缓存来提高查询速度,用空间换取时间。压缩是用时间换空间
cache应该选用什么数据结构?
便于查询的,且能快速找到数据的数据结构。
每次查询的时候,只要输入一致,就应该得到同样的结果(顺序也一致,例如减法函数,参数顺序不一致,结果不一样)
基于上面的分析,此数据结构应该是字典。
通过一个key,对应一个value。
key是参数列表组成的结构,value是函数返回值。难点在于key如何处理。
key的存储
key必须是hashable,不把可变类型作为参数
key能接收到位置参数和关键字参数传参
位置参数是被收集在一个tuple中的,本身就有顺序
关键字参数被收集在一个字典中,本身无序,这会带来一个问题,传参的顺序未必是字典中保存的顺序。如何解决?
OrderedDict行吗?可以,它可以记录顺序
不用OrderedDict行吗?可以,用一个tuple保存排过序的字典的item的kv对。
key的异同
什么才算是相同的key呢?
1 | import time |
定义一个加法函数,那么传参方式就应该有以下4种:
1 | 1. add(4,5) |
上面4种,可以有下面两种理解:
第一种:3和4相同,1、2、3都不同
第二种:1、2、3、4全部相同
lru_cache实现了第一种,可以看出单独的处理了位置参数和关键字参数。但是函数定义为def add(4,y=5),使用了默认值,如何理解add(4,5)和add(4)是否一样呢?如果认为一样,那么lru_cache无能为力。就需要使用inspect来自己实现算法
key的要求
key必须是hashable。由于key是所有实参组合而成,而且最好要作为key的,key一定要可以hash,但是如果key有不可hash类型数据,就无法完成。lru_cache就不可以。
1 | def add1(x,y): |
缓存必须使用key,但是key必须可hash,所以只能适用实参是不可变类型的函数调用。
key算法设计
inspect模块获取函数签名后,会得到parameters,这是一个有序字典,会保存所有参数的信息。我们用得到的有序字典构建一个params_dict,按照位置顺序从args中取出的参数与params_dict中的key是依次对应的,params_dict是参数名,args中是传入的实参,组成kv对,存入params_dict中。
如果是kwargs的值,就update到params_dict中。这是字典的更新方法
如果使用了缺省值的参数,不会出现在实参params_dict中,会出现在签名的取parameters中,缺省值也在定义中。
调用方式
普通的函数调用可以,但是过于明显,最好类似lru_cache的方式,让调用者无察觉的使用缓存。使用装饰器函数。
1 | from functools import wraps |
完成了key的生成。注意,这里使用了普通字典params_dict,先把位置参数的对应好,再填充关键字参数,最后补充缺省值,然后再排序生成key。
1 | from functools import wraps |
使用缓存
1 | # 实现过程: |
增加logger装饰器查看执行时间
1 | import time |
装饰器应用练习
一、实现一个cache装饰器,实现可过期被清除的功能
- 简化设计,函数的形参定义不包含可变位置参数、可变关键词参数和keyword-only参数
- 可以不考虑缓存满了之后的换出问题
1 | # 写代码时,先把主干写出来,把核心算法写出来以后,再一点点往上加功能 |
二、写一个命令分发器
- 程序员可以方便的注册函数到某一个命令,用户输入命令时,路由到注册的函数
- 用户输入用input(“>>>”)
1 | commands = {} |
三、实现base64解码
1 | alphabet = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" |
四、完善命令颁发器
- 实现函数可以带任意参数(可变参数除外),解析参数并要求用户输入
1 | # 即解决下面的问题 |
python基础练习-装饰器的应用