想要理解Python装饰器(Decorator)首先要了解闭包(closure)这一概念。
闭包(closure)
python是一种面向对象的编程语言,在python中一切皆对象,这样的话,变量拥有的属性函数也能拥有。如果是这样的话,那我们就知道在函数中再次创建函数也是可以的。这种函数我们称为内嵌函数。内嵌函数在其外部函数作用域内是可以被调用的,但是在外部函数的作用域外调用就是不合法的。
#coding:utf-8
#定义外部函数
def print_msg():
msg = "hello!"
def printer():
print (msg)
return (printer)
print_msg()
printer()
内部函数引用了外部函数(甚至是外层之外,但不是全局变量)此时内部函数就被称为闭包函数。如果闭包函数引用了外部定义变量被称为自由变量。闭包从语法上看非常简单,但是却有强大的作用。
#coding:utf-8
def count():
a=1
b=1
def sum():
c=1
print(b+c)#b为自由变量
return sum
总结:什么函数可以被称为闭包函数呢?主要是满足两点:函数内部定义的函数;引用了外部变量但非全局变量。
下面来看看Python中的装饰器。
装饰器
有了闭包的概念再去了解装饰器的概念。python的装饰器本质上就是一个函数,它可以让其他其他函数在不需要改变任何代码的情况下,增加额外的功能。装饰器的返回值也是一个函数对象。
装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后的函数的名字。内嵌函数负责修饰传入的函数。可以总结为以下几点;
实质:实质上是一个函数
参数:需要装饰的函数名
返回:装饰完的函数名
作用:不改变原函数的情况之下,增加额外的功能
特点:不需要对原函数进行修改
python装饰器的应用场景很多:插入日志,压力测试,事物处理,权限校验等等。其实装饰器最大的作用就是从我们已经写好的程序中抽离出一些功能相似的代码组建多个特定功能的装饰器,这样我们就可以针对不同的需求去使用特定的装饰器。这样就能去掉大量泛化的代码,使代码具有很清晰的逻辑。
函数装饰器
函数的函数装饰器
#coding:utf-8
import time
def decorator(func):
def wrapper(*args,**kwargs):
start_time=time.time()
func()
end_time=time.time()
print(end_time-start_time)
return wrapper
@decorator
def func():
time.sleep(0.8)
if __name__ == "__main__":
func()
在上面的代码中,func()函数是要被装饰的函数,装饰的目的就是显示func()函数的运行时间,@decorator这个语法就是相当于执行:func=decorator(func).
装饰器函数 - decorator,传入的参数是func(被装饰函数),返回的参数是内层函数wrapper,其实就相当于闭包函数,其作用就是给被传入函数进行装饰的作用。
wrapper参数为*args, **kwargs。*args表示参数以列表的形式传入,**kwargs表示参数以字典的方式传入。
类方法的函数装饰器
类方法的函数装饰器和函数的函数装饰器类似
#coding:utf-8
import time
def decorator(func):
def wrapper():
start_time=time.time()
func()
end_time=time.time()
print(end_time-start_time)
return wrapper
class Method():
@decorator
def func():
time.sleep(0.8)
if __name__ == "__main__":
Method.func()
类的装饰器
#coding:utf-8
import time
class Decorator():
def __init__(self, f):
self.f = f
def __call__(self):
start_time=time.time()
self.f()
end_time=time.time()
print(end_time-start_time)
class Method():
@decorator
def func():
time.sleep(0.8)
if __name__ == "__main__":
Method.func()
其中__call__(self)是一个特殊的方法,他可以将一个类实例变成一个可调用的对象。
p=Decorator(func)
p()
要使用类装饰器必须实现类中的__call__()方法,就相当于将实例变成了一个方法。
装饰器链
一个python函数也可以被多个装饰器修饰
#coding:utf-8
import time
from functools import wraps
class decorator():
def __init__(self, f):
self.f = f
def __call__(self):
start_time=time.time()
self.f()
end_time=time.time()
print(end_time-start_time)
def decorator_1(func):
@wraps(func)
def statn(*arg,**kwargs):
print("123")
print("234")
return statn
class Method():
@decorator
@decorator_1
def func():
time.sleep(0.8)
if __name__ == "__main__":
Method.func()
使用functools.wraps()函数,也可以实现被装饰函数的实例化