函数在python中作为一级对象,可以被当作参数传递,也可以作为函数返回,而装饰器就是利用这一特点,装饰器首先肯定是一个高阶函数,本质即是接受函数作为参数,返回一个函数。可以在函数执行的前后做一些操作,外层函数的作用域的变量(不包括全局变量)可以被内部函数应用,感觉又是一个闭包,但装饰器就是用一个函数装饰另一个函数,用来解决相同的一类问题。
理解装饰器
不带参数的装饰器
代码语言:txt复制import time
def timeit(func):
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
spend_time=time.time() - start_time
return spend_time
return inner
def sleep(x):
time.sleep(x)
deco = timeit(sleep)
deco(3)
3.003096103668213
结合闭包的概念,将函数作为参数传递,将很容易理解上述过程,执行过程:timeit(sleep) -> inner(3),将sleep函数作为参数传入timeit,timeit返回inner函数,根据闭包(函数 应用环境),在外层函数的应用环境被引入inner内层函数,所以很顺利的sleep函数与inner函数就建立了连接,所以所装饰器也可以说是一个闭包。
试试语法糖:
代码语言:txt复制import time
def timeit(func):
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
spend_time=time.time() - start_time
return spend_time
return inner
@timeit
def sleep(x):
time.sleep(x)
sleep(3)
3.0008034706115723
整个引用过程就更简单了,可以理解:sleep=timeit(sleep)
带参数的装饰器
代码语言:txt复制import time
def timeit(offset=0):
def outer(func):
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
spend_time=time.time() - start_time offset
return spend_time
return inner
return outer
def sleep(x):
time.sleep(x)
deco = timeit(1)
deco1 = deco(sleep)
deco1(3)
4.0014448165893555
根据不带参数的装饰器的理解,这个函数也是很好理解的。
试试语法糖:
代码语言:txt复制import time
def timeit(offset=0):
def outer(func):
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
spend_time=time.time() - start_time offset
return spend_time
return inner
return outer
@timeit(1)
def sleep(x):
time.sleep(x)
sleep(3)
4.0018510818481445
执行过程:timeit(1) -> outer(sleep) -> inner(3),其实说到这,装饰器就就是更多层的闭包。
多个装饰器叠加
首先我们要查看多个迭代器的的执行过程
代码语言:txt复制def deco_1(func):
print('enter deco_1')
def inner1(*args, **kwargs):
print('enter deco_1_inner')
print(func.__name__)
return func(*args, **kwargs)
return inner1
def deco_2(func):
print('enter deco_2')
def inner2(*args, **kwargs):
print('enter deco_2_inner')
print(func.__name__)
return func(*args, **kwargs)
return inner2
@deco_1
@deco_2
def Print(*args, **kwargs):
return args
enter deco_2
enter deco_1
Print(1, 2)
enter deco_1_inner
inner2
enter deco_2_inner
Print
(1, 2)
从这个例子中可以看出:Print(1, 2)=deco_1(deco_2(Print(1, 2)))
整个执行过程从下往上执行,注意看下输出的函数名的变化
不明白叠加装饰器的的使用场景,结合下面的使用场景看看:
代码语言:txt复制import sys
def checkpass(func):
def inner(*args, **kwargs):
if pass_database.get(user, None) == password:
func(*args, **kwargs)
else:
print('Password is not correct!')
sys.exit(2)
return inner
def checkuser(func):
def inner(*args, **kwargs):
if user in pass_database:
func(*args, **kwargs)
else:
print('User is not presenet')
sys.exit(3)
return inner
@checkuser
@checkpass
def index(user, password):
print("Hello {}!".format(user))
pass_database = {"dev": "1234567"}
user=input("请输入user:")
password=input("请输入pass:")
index(user, password)
类装饰器
使用类装饰器,是依靠类的魔术方法call实现,当使用@形式将装饰器附加到函数的时候就会调用此方法:
代码语言:txt复制class deco:
def __init__(self, func):
self._func = func
def __call__(self):
print('enter')
self._func()
print('exit')
@deco
def Print():
print(123)
保留原函数的信息
代码语言:txt复制import time
def timeit(func):
def inner(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs)
spend_time=time.time()-start_time
return spend_time
return inner
@timeit
def sleep(x):
''' sleep '''
time.sleep(x)
print(sleep.__name__,sleep.__doc__)
inner None
通过装饰器一装饰,原本sleep的函数信息就全部没了,成了一个新函数,而为了避免这种情况,functools中wraps可以将原本的函数属性全部更新到新函数中。
代码语言:txt复制from functools import wraps
import time
def timeit(func):
@wraps(func)
def inner(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs)
spend_time=time.time()-start_time
return spend_time
return inner
@timeit
def sleep(x):
''' sleep '''
time.sleep(x)
print(sleep.__name__,sleep.__doc__)
sleep sleep
以梦为马 不负韶华 归来仍是少年