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使用内存zipfile对象在内存中打包文件示例
Apr 30 Python
python简单实现旋转图片的方法
May 30 Python
python列表操作之extend和append的区别实例分析
Jul 28 Python
Python的爬虫包Beautiful Soup中用正则表达式来搜索
Jan 20 Python
Python heapq使用详解及实例代码
Jan 25 Python
Python利用itchat对微信中好友数据实现简单分析的方法
Nov 21 Python
python2.7和NLTK安装详细教程
Sep 19 Python
python合并已经存在的sheet数据到新sheet的方法
Dec 11 Python
Pytorch加载部分预训练模型的参数实例
Aug 18 Python
Python模块汇总(常用第三方库)
Oct 07 Python
基于Python 中函数的 收集参数 机制
Dec 21 Python
通过自学python能找到工作吗
Jun 21 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
PHP中空字符串介绍0、null、empty和false之间的关系
2012/09/25 PHP
php中解析带中文字符的url函数分享
2015/01/20 PHP
php开发微信支付获取用户地址
2015/10/04 PHP
PHP常见字符串操作函数与用法总结
2019/03/04 PHP
解析JavaScript中instanceof对于不同的构造器或许都返回true
2013/12/03 Javascript
使用Promise链式调用解决多个异步回调的问题
2017/01/15 Javascript
基于JavaScript实现选项卡效果
2017/07/21 Javascript
node作为中间服务层如何发送请求(发送请求的实现方法详解)
2018/01/02 Javascript
React路由管理之React Router总结
2018/05/10 Javascript
详解Vue源码学习之callHook钩子函数
2018/07/25 Javascript
详解mpvue scroll-view自动回弹bug解决方案
2018/10/01 Javascript
在vue项目中优雅的使用SVG的方法实例详解
2018/12/03 Javascript
webpack自动打包和热更新的实现方法
2019/06/24 Javascript
vue.js 打包时出现空白页和路径错误问题及解决方法
2019/06/26 Javascript
ES6基础之 Promise 对象用法实例详解
2019/08/22 Javascript
vue ssr服务端渲染(小白解惑)
2019/11/10 Javascript
[01:25]DOTA2自定义游戏灵园鬼域等你踏足
2015/10/30 DOTA
简洁的十分钟Python入门教程
2015/04/03 Python
python3 tkinter实现点击一个按钮跳出另一个窗口的方法
2019/06/13 Python
python实现在函数中修改变量值的方法
2019/07/16 Python
Python如何基于rsa模块实现非对称加密与解密
2020/01/03 Python
解决redis与Python交互取出来的是bytes类型的问题
2020/07/16 Python
浅谈CSS3中的变形功能-transform功能
2017/12/27 HTML / CSS
英国在线电子和小工具商店:TecoBuy
2018/10/06 全球购物
Regatta官网:英国最受欢迎的户外服装和鞋类品牌
2019/05/01 全球购物
泰国在线书店:SE-ED
2020/06/21 全球购物
家长会学生家长演讲稿
2013/12/29 职场文书
实习生求职自荐信
2014/02/07 职场文书
学历公证书范本
2014/04/09 职场文书
读书月活动方案
2014/05/22 职场文书
学校消防安全责任书
2014/07/23 职场文书
护士工作失误检讨书
2014/09/14 职场文书
建设工程授权委托书
2014/09/22 职场文书
荒岛余生观后感
2015/06/09 职场文书
深入浅析React中diff算法
2021/05/19 Javascript
阿里云日志过滤器配置日志服务
2022/04/09 Servers