视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
python装饰器-函数调用次数的方法(10s调用一次)
2020-11-27 14:22:01 责编:小OO
文档


下面为大家分享一篇python装饰器-函数调用次数的方法(10s调用一次),具有很好的参考价值,希望对大家有所帮助。一起过来看看吧

这是博主最近一家大公司的面试题,写一个装饰器,函数每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

下载本文
显示全文
专题