一、装饰器介绍下面先举个简单的栗子:
import time
def count_time(foo_func):
def inner():
print("inner func start!")
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func()
end_time = time.time()
print("the func cost:{:.2f}".format(end_time-start_time))
return inner
@count_time
def foo():
# 利用装饰器计算foo函数执行时间
print("foo execute done!!")
if __name__ == ‘__main__‘:
foo() # 调用foo函数
既然说增加了装饰器,那么原函数的代码和调用方式都没改变。
其中 @count_time 就等同于 foo = count_time(foo)
如果要给装饰器传参数。。如下:
import time
def outer(name):
def count_time(foo_func):
def inner():
print("inner func start!")
print("装饰器传进来的参数是:{}".format(name))
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func()
end_time = time.time()
print("the func cost:{:.2f}".format(end_time-start_time))
return inner
return count_time
@outer(name="jack")
def foo():
# 利用装饰器计算foo函数执行时间
print("foo execute done!!")
if __name__ == ‘__main__‘:
foo() # 调用foo函数
若要给装饰器传参数,只需要再给函数增加一层函数就行了。
但是由于使用装饰器后会改变函数的一些内置变量比如__name__,__doc__。看栗子:
import time
from functools import wraps
def count_time(foo_func):
# @wraps(foo_func)
def inner():
"""
now in inner func
"""
print("inner func start!")
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func()
end_time = time.time()
print("foo func cost:{:.2f}".format(end_time-start_time))
return inner
@count_time
def foo():
"""
now in foo func
"""
# 利用装饰器计算foo函数执行时间
print("foo func doc is {}".format(foo.__doc__)) # 输出 now in inner func
print("foo func name is {}".format(foo.__name__)) # 输出 inner
print("foo execute done!!")
if __name__ == ‘__main__‘:
foo() # 调用foo函数
所以需要纠正回来,该如何呢?来
只需要引入functools模块下的wraps函数
在inner函数上面使用@wraps(func),即再次装饰下inner函数即可
然后输出语句会变为:
print("foo func doc is {}".format(foo.__doc__)) # 输出 now in foo func
print("foo func name is {}".format(foo.__name__)) # 输出 foo
要给被装饰的函数传参数:
栗子如下:
import time
from functools import wraps
def count_time(foo_func):
@wraps(func)
def inner(*args, **kwargs):
"""
now in inner func
"""
print("inner func start!")
start_time = time.time()
time.sleep(1) # 1s后执行
foo_func(*args, **kwargs)
end_time = time.time()
print("foo func cost:{:.2f}".format(end_time-start_time))
return inner
@count_time
def foo(name, age, *args, **kwargs):
"""
now in foo func
"""
# 利用装饰器计算foo函数执行时间
print(name,age)
print(args,kwargs)
print("foo execute done!!")
if __name__ == ‘__main__‘:
foo("jack", 21, "student", department="信工学院") # 调用foo函数
增加的地方有调用时传了四个参数,分别是位置参数和关键字参数(最后一个是关键字参数,关键字参数必须在位置参数之后传入)
在装饰器内部的inner函数的参数那里增加了*args, **kwargs,和inner内部的func(*args, **kwargs)这里
args表示所有的位置参数都在args这个元组里面
kwargs表示所有的关键字参数都在args这个字典里面
二、基于类实现的装饰器
看栗子
class Logging(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("[DEBUG]: enter function {func}()".format(
func=self.func.__name__))
return self.func(*args, **kwargs)
@Logging
def say(something):
print( "say {}!".format(something))
say("hello")
输出:
[DEBUG]: enter function say()
say hello!
解答:如果用类来实现装饰器的话,必须实现类中的内置方法__init__和__call__这两个方法
其中__init__用来接收被装饰函数的名字,__call__用来对函数进行装饰以及增加额外的功能
若要给装饰器传参数。还是看栗子:
class logging(object):
def __init__(self, level=‘INFO‘):
self.level = level
def __call__(self, func): # 接受函数
def wrapper(*args, **kwargs):
print("[{level}]: enter function {func}()".format(
level=self.level,
func=func.__name__))
func(*args, **kwargs)
return wrapper # 返回函数
@logging(level=‘INFO‘)
def say(something):
print("say {}!".format(something))
say("hello")
这里的调用就是给装饰器传参数。
__init__方法参数接收的不再是被装饰函数的函数名,而是装饰器传来的参数
__call__方法参数接收被装饰函数的函数名,里面再嵌套一层函数用来接收装饰函数传的参数
最后,拓展一下wrapt这个装饰器:
import wrapt
@wrapt.decorator
def logging(wrapped, instance, args, kwargs): # instance is must need
print("[DEBUG]: enter {}()".format(wrapped.__name__))
return wrapped(*args, **kwargs)
@logging
def say(something): pass
say("hello")
这样看起来装饰器就更加明了了,,不用在装饰器内部再嵌套函数了。
上面的写法必须要按照上面的来
其中wrapped就表示被装饰的函数,
若要给装饰器传参数,也是再嵌套一层就可以了。