python装饰器-限制函数调用次数的方法(10s调用一次)


Posted in Python onApril 21, 2018

这是博主最近一家大公司的面试题,写一个装饰器,限制函数每10s调用一次。当时是笔试的,只写了大概的代码,回来后温习了python装饰器的基础知识,把代码写完了。决定写篇博客记录下。

装饰器分为带参数得装饰器以及不带参数得装饰器。

#不带参数的装饰器
@dec1
@dec2
def func():
  ...
#这个函数声明等价于
func = dec1(dec2(func))
#带参数的装饰器
@dec(some_args)
def func():
  ...
#这个函数声明等价于
func = dec(some_args)(func)

不带参数的装饰器需要注意的一些细节

1. 关于装饰器函数(decorator)本身

因此一个装饰器一般对应两个函数,一个是decorator函数,用来进行一些初始化操作处理,一个是decorated_func用来实现对被装饰的函数func的额外处理。并且为了保持对func的引用,decorated_func一般作为decorator的内部函数

def decorator(func):
  def decorator_func()
    func()
  return decorated_func

decorator函数只在函数声明的时候被调用一次

装饰器实际上是语法糖,在声明函数之后就会被调用,产生decorated_func,并把func符号的引用替换为decorated_func。之后每次调用func函数,实际调用的是decorated_func(这个很重要,装饰之后,其实每次调用的是decorated_func)。

>>> def decorator(func):
...   def decorated_func():
...     func(1)
...   return decorated_func
... 
#声明时就被调用
>>> @decorator
... def func(x):
...   print x
... 
decorator being called 
#使用func()函数实际上使用的是decorated_func函数
>>> func()
1
>>> func.__name__
'decorated_func'

如果要保证返回的decorated_func的函数名与func的函数名相同,应当在decorator函数返回decorated_func之前,加入decorated_func.name = func.name, 另外functools模块提供了wraps装饰器,可以完成这一动作。

#@wraps(func)的操作相当于
#在return decorated_func之前,执行
#decorated_func.__name__ = func.__name__
#func作为装饰器参数传入, 
#decorated_func则作为wraps返回的函数的参数传入
>>> def decorator(func):
...   @wraps(func)
...   def decorated_func():
...     func(1)
...   return decorated_func
... 
#声明时就被调用
>>> @decorator
... def func(x):
...   print x
... 
decorator being called 
#使用func()函数实际上使用的是decorated_func函数
>>> func()
1
>>> func.__name__
'func'

decorator函数局部变量的妙用

因为closure的特性(详见(1)部分闭包部分的详解),decorator声明的变量会被decorated_func.func_closure引用,所以调用了decorator方法结束之后,decorator方法的局部变量也不会被回收,因此可以用decorator方法的局部变量作为计数器,缓存等等。

值得注意的是,如果要改变变量的值,该变量一定要是可变对象,因此就算是计数器,也应当用列表来实现。并且声明一次函数调用一次decorator函数,所以不同函数的计数器之间互不冲突,例如:

#!/usr/bin/env python
#filename decorator.py
def decorator(func):
  #注意这里使用可变对象
  a = [0]
  def decorated_func(*args,**keyargs):
    func(*args, **keyargs)
    #因为闭包是浅拷贝,如果是不可变对象,每次调用完成后符号都会被清空,导致错误
    a[0] += 1
    print "%s have bing called %d times" % (func.__name__, a[0])
  return decorated_func
@decorator
def func(x):
  print x
@decorator
def theOtherFunc(x):
  print x

下面我们开始写代码:

#coding=UTF-8
#!/usr/bin/env python
#filename decorator.py
import time
from functools import wraps
def decorator(func):
  "cache for function result, which is immutable with fixed arguments"
  print "initial cache for %s" % func.__name__
  cache = {}
  @wraps(func)
  def decorated_func(*args,**kwargs):
    # 函数的名称作为key
    key = func.__name__
    result = None
    #判断是否存在缓存
    if key in cache.keys():
      (result, updateTime) = cache[key]
      #过期时间固定为10秒
      if time.time() -updateTime < 10:
        print "limit call 10s", key
        result = updateTime
      else :
        print "cache expired !!! can call "
        result = None
    else:
      print "no cache for ", key
    #如果过期,或则没有缓存调用方法
    if result is None:
      result = func(*args, **kwargs)
      cache[key] = (result, time.time())
    return result
  return decorated_func
@decorator
def func(x):
  print 'call func'

随便测试了下,基本没有问题。

>>> from decorator import func
initial cache for func
>>> func(1)
no cache for func
call func
>>> func(1)
limit call 10s func
1488082913.239092
>>> func(1)
cache expired !!! can call
call func
>>> func(1)
limit call 10s func
1488082923.298204
>>> func(1)
cache expired !!! can call
call func
>>> func(1)
limit call 10s func
1488082935.165979
>>> func(1)
limit call 10s func
1488082935.165979

以上这篇python装饰器-限制函数调用次数的方法(10s调用一次)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
在Python程序中实现分布式进程的教程
Apr 28 Python
python中json格式数据输出的简单实现方法
Oct 31 Python
django框架如何集成celery进行开发
May 24 Python
Python中py文件引用另一个py文件变量的方法
Apr 29 Python
ubuntu17.4下为python和python3装上pip的方法
Jun 12 Python
Python玩转PDF的各种骚操作
May 06 Python
python多进程下实现日志记录按时间分割
Jul 22 Python
Python数据可视化实现正态分布(高斯分布)
Aug 21 Python
python绘制彩虹图
Dec 16 Python
Scrapy框架基本命令与settings.py设置
Feb 06 Python
python中读入二维csv格式的表格方法详解(以元组/列表形式表示)
Apr 24 Python
如何以Winsows Service方式运行JupyterLab
Aug 30 Python
对Python中的@classmethod用法详解
Apr 21 #Python
python3+dlib实现人脸识别和情绪分析
Apr 21 #Python
Python通过属性手段实现只允许调用一次的示例讲解
Apr 21 #Python
使用Python和xlwt向Excel文件中写入中文的实例
Apr 21 #Python
使用pandas读取csv文件的指定列方法
Apr 21 #Python
Python 3.7新功能之dataclass装饰器详解
Apr 21 #Python
pandas or sql计算前后两行数据间的增值方法
Apr 20 #Python
You might like
mcrypt启用 加密以及解密过程详细解析
2013/08/07 PHP
PHP程序漏洞产生的原因分析与防范方法说明
2014/03/06 PHP
Linux系统中设置多版本PHP共存配合Nginx服务器使用
2015/12/21 PHP
php实现的二分查找算法示例
2017/06/20 PHP
laravel多条件查询方法(and,or嵌套查询)
2019/10/09 PHP
一个JS小玩意 几个属性相加不能超过一个特定值.
2009/09/29 Javascript
javascript 图片上一张下一张链接效果代码
2010/03/12 Javascript
Javascript类定义语法,私有成员、受保护成员、静态成员等介绍
2011/12/08 Javascript
NodeJS与Mysql的交互示例代码
2013/08/18 NodeJs
完美解决AJAX跨域问题
2013/11/01 Javascript
javascript实现youku的视频代码自适应宽度
2015/05/25 Javascript
jQuery实现灰蓝风格标准二级下拉菜单效果代码
2015/08/31 Javascript
JavaScript使用DeviceOne开发实战(一) 配置和起步
2015/12/01 Javascript
分享jQuery网页元素拖拽插件
2020/12/01 Javascript
Javascript将JSON日期格式化
2016/08/23 Javascript
解析JavaScript实现DDoS攻击原理与保护措施
2016/12/26 Javascript
原生js实现手风琴功能(支持横纵向调用)
2017/01/13 Javascript
微信小程序动态增加按钮组件
2018/09/14 Javascript
微信小程序canvas绘制圆角base64图片的实现
2019/08/18 Javascript
js实现圆形显示鼠标单击位置
2020/02/11 Javascript
基于Vue CSR的微前端实现方案实践
2020/05/27 Javascript
Python+Opencv识别两张相似图片
2020/03/23 Python
Python面向对象编程基础解析(二)
2017/10/26 Python
Python之Scrapy爬虫框架安装及简单使用详解
2017/12/22 Python
Iconfont(矢量图标)+iconmoon(图标svg互转)配合javascript实现社交分享系统
2020/04/21 Python
Python+Selenium实现自动化的环境搭建的步骤(图文)
2020/09/01 Python
沃达丰英国有限公司:Vodafone英国
2019/04/16 全球购物
美国环保妈妈、儿童和婴儿用品购物网站:The Tot
2019/11/24 全球购物
高级销售求职信
2014/02/21 职场文书
教师爱岗敬业演讲稿
2014/05/05 职场文书
怀孕辞职信怎么写
2015/02/28 职场文书
转正申请报告格式
2015/05/15 职场文书
安全事故隐患排查治理制度
2015/08/05 职场文书
情侣餐厅的创业计划书范本!
2019/07/26 职场文书
MySQL命令无法输入中文问题的解决方式
2021/08/30 MySQL
nginx从安装到配置详细说明(安装,安全配置,防盗链,动静分离,配置 HTTPS,性能优化)
2022/02/12 Servers