一丶写一个命令分发器

1.要求:程序员可以方便的注册函数到某一命令,用户输入命令,路由到注册的函数,如果此命令没有对应的注册函数,执行默认函数

拿到这个题目,又是一脸懵逼

分析:题目要求大概可以分成两个部分,注册函数,执行函数。

# 注册函数
def command():functionname = {}def register(name): #注册函数def wrapper(fn):# functionname[fn.__name__] = fn 如果这样设置的话,路由的函数名只能是函数的__name__functionname[name] = fnreturn fnreturn wrapperdef defaultfunc(): # 默认函数,用来处理仓库命令里没有cmd时的执行函数print(" Unknown cmd!")def run():cmd = input(">>")cmd = cmd.strip()return functionname.get(cmd,defaultfunc)()return register, runregister, run = command()@register("add")
def add():print("register!")
2.完善上面的命令分发器,实现函数可以带任意参数(可变参数除外),解析参数并要求用户输入

思路:
1°注册的时候固定死函数的参数
2°运行时,通过input输入的字符串获取参数

下面先来实现第一种:

def command():functionname = {} # 注册函数def register(name, *args, **kwargs):def wrapper(fn):# 将函数和参数收集,用的时候解包即可functionname[name] = fn, args, kwargs  return fnreturn wrapper# 默认函数def defaultfunc():print("Unknown cmd!")# 执行函数def run():cmd = input(">>")# 因为可能存在找不到cmd的情况,讲缺省值设为一个三元组用来解包, 用来执行default()函数fn, args, kwargs = functionname.get(cmd,(defaultfunc, (), {}))fn(*args, **kwargs)return register, runregister, run = command()@register("add1",100,200)
@register("add2",10,20)
@register("add3",1,2)
def add(x, y):print("register!")return x + y

下面来实现第二种,通过input的收集来获取参数:

def command():functionname = {}# 注册函数def register(name):  def wrapper(fn):# functionname[fn.__name__] = fn 如果这样设置的话,路由的函数名只能是自己的名字functionname[name] = fnreturn fnreturn wrapper# 默认函数def defaultfunc():print(" Unknown cmd!")def run():cmd = input(">>")cmd, *params = cmd.replace(","," ").split()  # 参数解构args = [] # 用来收集位置参数kwargs = {} # 用来收集关键字参数for i in params: # params类似[1,"x"=2]item = i.split("=") # 返回值为listif len(item) == 1:args.append(int(i))   # 假设用户想输入的是数字else:a = []item[1] = int(item[1])  # 假设用户想输入的是数字a.append(item) kwargs.update(a) return functionname.get(cmd,defaultfunc)(*args, **kwargs)return register, runregister, run = command()@register("add")
def add(x,y=100):print("register!")return x + y

很多Python Web框架使用这样的装饰器把函数是添加到某种中央注册处,例如把URL模式映射到HTTP响应的函数上的注册处,这种注册装饰器可能会也可能不会修改被装饰的函数。

二丶实现一个cache装饰器,可先可过期被清除的功能

数据类型的选择:

缓存的应用场景,是有数据需要频繁查询,且每次都需要大量计算或者等待时间之后才能返回的结果的情况,使用cache缓存来提高查询速度,用内存空间换区查询、加载的时间。

cache应该选择什么数据结构?
  • 便有查询的,且能快速获取数据的数据结构。
  • 每次查询的时候,只要输入一致,就能得到同样的结果(这里的输入一致,对于functools中的lru_cache来说就非常严格,但有些时候,我们只要求形参对应的实参的值一致就满足要求,这将是我们下面make_key的重点)。
  • 我们应该选择一个映射结构,这里索性就选择字典,key是参数列表组成的结构,value是函数返回值。
key的存储
  • 字典的key必须是hashabke对象,key能接收位置参数和关键字参数传参
  • 可以将形参和实参绑定在一起,放在字典params_dict中,最后取出items对,实参不可以出现unhashable对象
key的算法设计
  • inspect模块获取函数签名后,取parameters,这是一个有序字典,会保存所有参数的信息。
  • 构建一个字典params_dict用来存放函数的实参和形参绑定在一次,组成kv键值
  • 如果使用了缺省值的参数,不会出现在实参params_dict中,会出现的parameters中,缺省值也在函数定义中,因此要先将paramster中的带有缺省值的kv存在在字典中
# 代码实现make_key
def make_key(fn,args, kwargs):params_key = {}sig = inspect.signature(fn)  # 制作签名params = sig.parameters  # 得到属性值,是一个键值对# 首先现将所有形参和对应的缺省值的kv对放到params_dict中for j in params.keys():params_key[j] = params[j].default# 下面的这种更新params_key略显臃肿# params_key.update(zip(params.keys(),args))  # 将顺序传参的参数和形参按位置对应关系绑定在一起# params_key.update(kwargs) # 将传的关键字参数放入dict_key中# sig.bind(*args, **kwargs) 将函数实参和形参绑定在一起bound_values = sig.bind(*args, **kwargs).arguments # 返回值为OrderDictparams_key.update(bound_values.items())return tuple(params_key.items())

有了make_key我们就可以很轻松的实现想要的cache功能

def make_key(fn,args, kwargs):params_key = {}sig = inspect.signature(fn)  # 制作签名params = sig.parameters  # 得到属性值,是一个键值对for j in params.keys(): # 先用用缺省参数更新一下dict_keyparams_key[j] = params[j].default# 下面的这种更新dict_key略显臃肿# params_key.update(zip(params.keys(),args))  # 将顺序传参的参数和形参按位置对应关系绑定在一起# params_key.update(kwargs) # 将传的关键字参数放入dict_key中# sig.bind(*args, **kwargs) 将函数实参和形参绑定在一起,下面更新到字典中bound_values = sig.bind(*args, **kwargs).argumentsparams_key.update(bound_values.items())return tuple(params_key.items())def decorator(fun):cache = {}@functools.wraps(fun)def wrapper(*args, **kwargs):key = make_key(fun,args, kwargs)# 查询cache是否存在已有的键值对if key in cache: return cache[key]cache[key] = fun(*args, **kwargs)return cache[key]return wrapper@decorator
def add(x=100,y=100):time.sleep(2)return x + y
过期功能

一般缓存系统都有过期功能。
过期是什么?
他是某一个key过期。可以对每一个key单独设置过期时间,也可以对这些key统一设定过期时。
本次的实现采用同一设定key过期的时间,当key的生存超过了这个时间,就自动被清除。
注意:这里并没有考虑线程等问题。而且这种过期机制,每一次都要遍历所有数据,大量数据的时候,遍历可能有效率问题。

清除的时机

何时清除过期key?
1.用到某个key之前,先判断是否过期,如果过期重新调用函数生成新的key对应value值。
2.一个线程负责清除过期的key,这个以后实现,本次在创建key之前,清除所有的key。

value的设计
1、key =>(v,createtimestamp)
适合key过期时间都是统一的设定。
2、key => (v,createtimestamp,duration)
duration是过期时间,这样每一个key就可以单独控制过期时间。在这种设计中,-1表示永不过期,0表示立即过期,正整数表示持续一段时间过期。
本次采用第一种实现。

def make_key(fn,args, kwargs):params_key = {}sig = inspect.signature(fn)  # 制作签名params = sig.parameters  # 得到属性值,是一个键值对for j in params.keys(): # 先用用缺省参数更新一下dict_keyparams_key[j] = params[j].default# 下面的这种更新dict_key略显臃肿# params_key.update(zip(params.keys(),args))  # 将顺序传参的参数和形参按位置对应关系绑定在一起# params_key.update(kwargs) # 将传的关键字参数放入dict_key中# sig.bind(*args, **kwargs) 将函数实参和形参绑定在一起,下面更新到字典中bound_values = sig.bind(*args, **kwargs).argumentsparams_key.update(bound_values.items())return tuple(params_key.items())def cache_experid(duration = 5):def decorator(fun):cache = {}@functools.wrap(fun)def wrapper(*args, **kwargs):# 每次使用前,批量清除过期的cache,cache是一个字典,切记,不能边遍历边修改expired = []for i in cache: nowstamp = datetime.datetime.now().timestamp()if nowstamp - cache[i][1]  > duration:expired.append(i)for j in expired:cache.pop(j)key = make_key(fun,args, kwargs)if key in cache:return cache[key]cache[key] = fun(*args, **kwargs), datetime.datetime.now().timestamp()return cache[key]return wrapperreturn decorator@cache_experid()
def add(x=100,y=100):time.sleep(2)return x + y

装饰器的用途

装饰器是AOP面向切面变成Aspect Oriented Programming的思想的体现。
面向对象往往需要通过集成或者组合依赖等方式调用一些功能,这些功能的代码旺旺可能在多个类中出现。
装饰器应用在日志、监控、权限、审计、参数检查、路由等处理。
这些功能和业务功能无关,是很多业务都需要的公共的功能,所以独立出来,需要的时候,对目标进行增强。

Python装饰器应用实例相关推荐

  1. python装饰器实例-python装饰器使用实例详解

    这篇文章主要介绍了python装饰器使用实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 python装饰器的作用就是在不想改变原函数代码的情 ...

  2. python装饰器详解-python装饰器使用实例详解

    这篇文章主要介绍了python装饰器使用实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 python装饰器的作用就是在不想改变原函数代码的情 ...

  3. python装饰器实例-Python装饰器用法实例总结

    本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能, ...

  4. python装饰器作用-Python装饰器用法实例总结

    一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.简单的说装饰器就是一个用来返回函数的函数 ...

  5. python装饰器详解51-python装饰器使用实例详解

    这篇文章主要介绍了python装饰器使用实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 python装饰器的作用就是在不想改变原函数代码的情 ...

  6. Python装饰器详解,详细介绍它的应用场景

    装饰器的应用场景 附加功能 数据的清理或添加: 函数参数类型验证 @require_ints 类似请求前拦截 数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改 为函数提供额外的数据 ...

  7. python装饰器实例-基于Python 装饰器装饰类中的方法实例

    title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] categ ...

  8. python装饰器实例-python装饰器实例大详解

    原标题:python装饰器实例大详解 一.作用域 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我 ...

  9. python装饰器实例-Python装饰器原理与简单用法实例分析

    本文实例讲述了Python装饰器原理与简单用法.分享给大家供大家参考,具体如下: 今天整理装饰器,内嵌的装饰器.让装饰器带参数等多种形式,非常复杂,让人头疼不已.但是突然间发现了装饰器的奥秘,原来如此 ...

最新文章

  1. 聊聊分布式定时任务中间件架构及其实现--转
  2. 程序员晒工资单,还是大厂香!据说大多数3年,35K还少了?
  3. 线程/协程/异步的编程模型(CPU利用率为核心)
  4. vscode中PyLint报错Unable to import解决方案
  5. 怎么在我的世界服务器注册,我的世界服务器怎么注册
  6. MDK寄存器地址映射分析
  7. MTK 移植泰文输入法
  8. duration java_Java Duration类| ofMinutes()方法与示例
  9. InputStreamReader和 OutputStreamWriter
  10. TCP/IP详解 笔记九
  11. UVALive 3958 Weird Numbers (负进制数)
  12. JQuery Ajax 在asp.net中使用总结
  13. 判别模型、生成模型和朴素贝叶斯模型
  14. Android开发之多线程编程Thread和Runnable使用
  15. java 新手入门电子书_3款针对初学者的免费Java电子书
  16. Linux 端蓝牙调试
  17. LTE系统信息 --- MIB、SIB
  18. win7新建ios开发环境
  19. ensp启动路由 40错误-已解决
  20. 微信公众号图文编辑新手教程

热门文章

  1. python 开发浏览器插件,利用firebreath开发跨浏览器插件
  2. Scipy文件输入/输出mat,wav,mp3
  3. python输入数字比大小_Python练习实例47 | 比较任意两个数字的大小
  4. MindMaster:程序员的脑图工具
  5. python自动修图软件_3 行 Python 代码实现 5 秒抠图的 AI 神器,告别PS(附教程)...
  6. ASEMI三相整流桥和单相整流桥的详细对比
  7. Android屏幕兼容性概览
  8. 文件或目录损坏且无法读取 chkdsk修复方法
  9. FireSim简介-亚马逊云-伯克利合作项目-公共云中FPGA加速的周期精确扩展系统仿真-ISCA18
  10. 【密】设计院“图框”制作