Python的修饰器(decorator)是一个非常强大的功能,一种优秀的设计模式,将重复的内容抽象出来,赋予一个函数其他的功能,但又不去改变函数自身。使得代码极其简洁,易于维护。
为了能够初步了解修饰器的作用,我们先从一个简单的例子开始。
1.simple example
现在有很多个函数,我们需要得到每个函数的运行时间。为了简单描述,我只用一个函数举例子。函数功能很简单
def foo():
print('hello')
现在想给这个函数计算一下执行时间
def foo():
t1 = time()
print('hello')
t2 = time()
print(t2-t1)
但这种办法是不是特别笨,假如我现在不想要这个功能了,我还需要注释掉删除一些内容。有没有不修改foo函数,同时能完成计时的功能。
当然有,修饰器就是做这个事情的。
def run_time(func):
def warp():
t1 = time()
func() # 执行函数
t2 = time()
print(t2-t1)
return warp
我们写了一个闭包,run_time接受唯一的参数,这个参数是某个要被修饰的函数对象func,然后warp函数就是要加上修饰内容,并在其中执行这个被修饰的函数。
接下来我们只要这样使用,
foo = run_time(foo)
foo()
用run_time包装的foo就具有计时功能了。但python支持另一种更加简洁的写法。
@run_time
def foo():
print('hello')
@叫做语法糖,在函数定义前加上@decorator_name, 那么函数foo执行之前会跳到run_time,执行run_time的内容。其中run_time返回的闭包函数,内部也会执行foo的内容。如果不想要这个功能,仅需要注释掉一行就行了。
现在再来看一种复杂一点的情况。如果foo函数有返回值怎么办?
被修饰的函数有返回值
def foo():
return 666
则只要闭包函数中执行的func也有返回值,则闭包函数需要返回func的返回值。
def run_time(func):
def warp():
# do somethings.
temp = func() # 执行函数
return temp # 需要返回func的返回值
return warp
被修饰函数带参数的情况
def foo(a, b, c):
return (a,b,c)
则warp函数需要用*args,**kwargs
def run_time(func):
def warp(*args, **kwargs):
# do somethings.
temp = func(*args, **kwargs) # 执行函数
return temp
return warp
实验一下
@run_time
def foo(a, b, c):
return (a,b,c)
print(foo(1,2,3))
# 打印内容如下
# (1, 2, 3)
更复杂的情况
有的时候我们不仅想用修饰器把重复内容抽取出来,还想让同一个修饰器完成不同的功能。那么我们就需要给修饰器加上参数了。
比如我们想让修饰器打印正在运行函数的名称
def decorator_with_param(name): # 修饰器的参数
def run_time(func): # func为要修饰的函数对象
def warp(*args, **kwargs):
# do somethings.
print(name)
temp = func(*args, **kwargs) # 执行函数
return temp
return warp
return run_time
@decorator_with_param('foo') # 把函数名传进去
def foo(a, b, c):
return (a,b,c)
@decorator_with_param('another_foo') # 把函数名传进去
def another_foo(a, b, c):
return (a,b,c)
print(foo(1,2,3))