作者:Zarten 知乎专栏:Python基础深入详解 知乎ID: Zarten 简介: 互联网一线工作者,尊重原创并欢迎评论留言指出不足之处,也希望多些关注和点赞是给作者最好的鼓励 !
概述
装饰器其实就是一个函数,返回值是一个函数对象,它可以让其他函数在不需要做任何代码改动的前提下增加额外功能,比如:日志功能、性能测试、缓存、事务处理、权限校验等。
为了理解装饰器,我们要先理解一个概念,即:python里面一切皆对象,包括函数。
函数装饰器详解
最简单的装饰器
def my_decorator(f):
return f
这个装饰器什么也没做,返回原函数对象。
装饰器的使用
def my_decorator(f):
return f
@my_decorator
def fun():
print('zarten')
fun()
上面代码等价于:
def my_decorator(f):
return f
def fun():
print('zarten')
fun = my_decorator(fun)
fun()
一般使用第一种方式,python中特有的装饰器语法,更加简洁。
装饰器返回另外一个函数
装饰器通常都是返回另外一个函数,而不是原函数本身
def my_decorator(f):
def other_fun():
print('other_fun')
return other_fun
@my_decorator
def fun():
print('zarten')
fun()
print(fun)
调用fun()函数,其实是运行装饰器里面的other_fun()函数。从输出结果也可看到,fun()函数现在是other_fun()函数的引用。
从输出结果可以看到,并没有执行fun()函数内的代码,这是因为在装饰器内部执行了other_fun()函数,但并没有在内部执行fun()函数,所以一般在装饰器传入参数,这个参数是被装饰的函数对象,都会在装饰器内部调用并返回其结果
def my_decorator(f):
def other_fun():
print('other_fun')
return f()
return other_fun
@my_decorator
def fun():
print('zarten')
fun()
print(fun)
functools.wraps的作用
我们知道,执行fun()函数,实际上是执行了装饰器里面的other_fun()函数,并在内部返回了原函数。但这样有个缺点是:原函数的一些元信息发生了改变,如docstring、__name__、参数列表等。如下代码所示:
def my_decorator(f):
def other_fun():
print('other_fun')
return f()
return other_fun
@my_decorator
def fun():
print('zarten')
print('fun name:', fun.__name__)
为了防止元信息改变,python引入了functools.wraps,用法如下
from functools import wraps
def my_decorator(f):
@wraps(f)
def other_fun():
print('other_fun')
return f()
return other_fun
@my_decorator
def fun():
print('zarten')
print('fun name:', fun.__name__)
一个完整简单的日志打印装饰器
from functools import wraps
def my_decorator(f):
@wraps(f)
def other_fun(*args, **kwargs):
print('%s in running' % f.__name__)
return f(*args, **kwargs)
return other_fun
@my_decorator
def fun():
print('zarten')
fun()
带参数的装饰器
装饰器是把被装饰函数作为第一个参数传进去,若装饰器带有参数时,怎么接收其他额外的参数呢?
方法是:将额外的参数作为第一个参数传进去,然后返回一个函数对象,这个函数的参数才是被装饰函数的对象
from functools import wraps
def my_decorator(zhihu_id= 'zarten'):
def decorator(f):
@wraps(f)
def other_fun(*args, **kwargs):
print('%s in running.name is %s' % (f.__name__, zhihu_id))
return f(*args, **kwargs)
return other_fun
return decorator
@my_decorator('zarten_2')
def fun():
print('zarten')
fun()
从上面代码可以看到,装饰器使用的默认参数,若装饰器不传递任何参数时,也需要使用空括号,如@my_decorator()
类装饰器详解
类装饰器,也就是说装饰器是一个类了,而不是一个函数了。类装饰器和函数装饰器非常类似。
可以利用类的特殊方法__call__()来实现,实现此特殊方法后,类对象可以像函数一样调用。PS:若对python中的常用特殊方法不了解的,可以查看我之前写的文章,点这里。
类装饰器的实现
class Decorator():
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
print('%s in running' % self.f.__name__)
self.f(*args, **kwargs)
使用类装饰器
使用类装饰器跟使用函数装饰也是非常的类似
@Decorator
def fun():
print('zarten')
fun()
此时被装饰的函数对象作为装饰器类的参数传入,返回一个类装饰器的对象,由于装饰器类里实现了__call__()的特殊方法,固可以直接调用这个对象。
若不使用@Decorator语法糖,上面代码等价于:
def fun():
print('zarten')
fun = Decorator(fun)
fun()
完整代码如下:
class Decorator():
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
print('%s in running' % self.f.__name__)
self.f(*args, **kwargs)
@Decorator
def fun():
print('zarten')
fun()