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 分析Nginx访问日志并保存到MySQL数据库实例
Mar 13 Python
Python中给List添加元素的4种方法分享
Nov 28 Python
使用url_helper简化Python中Django框架的url配置教程
May 30 Python
一些常用的Python爬虫技巧汇总
Sep 28 Python
python使用xlrd与xlwt对excel的读写和格式设定
Jan 21 Python
pandas 两列时间相减换算为秒的方法
Apr 20 Python
Tensorflow中的placeholder和feed_dict的使用
Jul 09 Python
pandas通过索引进行排序的示例
Nov 16 Python
python获取txt文件词向量过程详解
Jul 05 Python
python_array[0][0]与array[0,0]的区别详解
Feb 18 Python
Python基础教程之输入输出和运算符
Jul 26 Python
pycharm 实现光标快速移动到括号外或行尾的操作
Feb 05 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自动生成月历代码
2006/10/09 PHP
谈PHP生成静态页面分析 模板+缓存+写文件
2009/08/17 PHP
来自phpguru得Php Cache类源码
2010/04/15 PHP
PHP源代码数组统计count分析
2011/08/02 PHP
php批量添加数据与批量更新数据的实现方法
2014/12/16 PHP
PHP文件操作详解
2016/12/30 PHP
php5对象复制、clone、浅复制与深复制实例详解
2019/08/14 PHP
手机号码,密码正则验证
2014/09/04 Javascript
node.js中的fs.closeSync方法使用说明
2014/12/17 Javascript
JavaScript中的对象序列化介绍
2014/12/30 Javascript
js随机生成网页背景颜色的方法
2015/02/26 Javascript
jQuery实现公告新闻自动滚屏效果实例代码
2016/07/14 Javascript
ionic实现带字的toggle滑动组件
2016/08/27 Javascript
Angular.js中用ng-repeat-start实现自定义显示
2016/10/18 Javascript
jquery与ajax获取特殊字符实例详解
2017/01/08 Javascript
将angular.js项目整合到.net mvc中的方法详解
2017/06/29 Javascript
浅谈vue-router2路由参数注意的问题
2017/11/08 Javascript
vue自定义指令实现仅支持输入数字和浮点型的示例
2019/10/30 Javascript
基于JavaScript获取url参数2种方法
2020/04/17 Javascript
vue 实现一个简单的全局调用弹窗案例
2020/09/10 Javascript
JavaScript常用工具函数库汇总
2020/09/17 Javascript
Python列表生成器的循环技巧分享
2015/03/06 Python
深入理解Python中命名空间的查找规则LEGB
2015/08/06 Python
Django 限制用户访问频率的中间件的实现
2018/08/23 Python
Python符号计算之实现函数极限的方法
2019/07/15 Python
pandas DataFrame行或列的删除方法的实现示例
2019/08/02 Python
python实现sm2和sm4国密(国家商用密码)算法的示例
2020/09/26 Python
ivx平台开发之不用代码实现一个九宫格抽奖功能
2021/01/27 HTML / CSS
美国男士内衣品牌:Tommy John
2017/12/22 全球购物
沙特阿拉伯排名第一的在线时尚购物应用程序:1Zillion
2020/08/08 全球购物
2014年学校安全工作总结
2014/11/13 职场文书
三孔导游词
2015/02/05 职场文书
教师考核鉴定意见
2015/06/05 职场文书
2015年汽车销售员工作总结
2015/07/24 职场文书
教师外出学习心得体会
2016/01/18 职场文书
MySQL索引 高效获取数据的数据结构
2022/05/02 MySQL