什么是装饰器?
实例代码1:不传递参数
import time
def decor(fn):
def wrapper():
begin_t = time.time()
fn()
print("spent time:", time.time() - begin_t)
return wrapper
@decor
def demo():
n = 10000
ret = 0
for n in range(n):
ret += n
print("result:", ret)
return
print(demo.__name__)
demo()
wrapper
result: 49995000
spent time: 0.0019981861114501953
实例代码2:传递一个参数
import time
def decor(fn):
def wrapper(n):
begin_t = time.time()
fn(n)
print("spent time:", time.time() - begin_t)
return wrapper
@decor
def demo(n):
ret = 0
for n in range(n):
ret += n
print("result:", ret)
return
print(demo.__name__)
demo(100000)
wrapper
result: 49995000
spent time: 0.0019981861114501953
实例代码2:传递两个参数
import time
def decor(fn):
def wrapper(m, n):
begin_t = time.time()
fn(m, n)
print("spent time:", time.time() - begin_t)
return wrapper
@decor
def demo(m, n):
ret = 0
for i in range(m, n):
ret += i
print("result:", ret)
return
print(demo.__name__)
demo(10, 200000)
wrapper
result: 19999899955
spent time: 0.04311800003051758
实例代码3:装饰器带有参数时
import time
from functools import wraps
def decrator(t):
print("time:", t.time())
def decor(fn):
@wraps(fn)
def wrapper():
print('修饰器1:begin')
fn()
print('修饰器1:end')
return wrapper
return decor
@decrator(t=time)
def demo():
n = 10000
ret = 0
for n in range(n):
ret += n
print("result:", ret)
return
print(demo.__name__)
demo()
被装饰的函数体是如何传入内层的?
装饰器有多个参数,需要以@decrator(t=time)的方式使用,这个时候decrator是已经执行了的(因为加了括号),可以粗略的理解为加载被修饰函数上的是decor,所以这和普通的装饰器并无差别
优化传递的参数
import time
from functools import wraps
def decor(fn):
@wraps(fn)
def wrapper(*args):
begin_t = time.time()
fn(*args)
print("spent time:", time.time() - begin_t)
return wrapper
@decor
def demo(m, n):
ret = 0
for i in range(m, n):
ret += i
print("result:", ret)
return
print(demo.__name__)
demo(10, 200000)
- 函数装饰函数
- 函数装饰类
- 类装饰函数
- 类装饰类
(1)函数装饰函数
(2)函数装饰类
import time
from functools import wraps
def decor(fn):
@wraps(fn)
def wrapper(*args):
ret = fn(*args)
return ret
return wrapper
@decor
class demo(object):
def __init__(self, key, value):
self.key = key
self.value = value
def __str__(self):
return "<{}={}>".format(self.key, self.value)
print(demo.__name__)
res = demo('name', 'Tom')
print(res)
demo
<name=Tom>
(3)类装饰函数
import time
from functools import wraps
class decor():
def __init__(self, fn):
self.__fn = fn
def __call__(self, *args, **kwargs):
return self.__fn(*args, **kwargs)
@decor
def demo(m, n):
ret = 0
for i in range(m, n):
ret += i
print("result:", ret)
return
print(type(demo))
demo(10, 200000)
(4)类装饰类
import time
from functools import wraps
class decor():
def __init__(self, cls):
self.wrpper_class = cls
def __call__(self, *args, **kwargs):
if 'key' in kwargs.keys():
kwargs['key'] = 'wrapper_' + kwargs['key']
return self.wrpper_class(*args, **kwargs)
@decor
class demo(object):
def __init__(self, key=None, value=None):
self.key = key
self.value = value
def __str__(self):
return "<{}={}>".format(self.key, self.value)
print(type(demo))
res = demo(key='name', value='Tom')
print(res)
(6)多个修饰函数
import time
from functools import wraps
def decor2(fn):
@wraps(fn)
def wrapper2():
print('修饰器2:begin')
fn()
print('修饰器2:end')
return wrapper2
def decor(fn):
@wraps(fn)
def wrapper():
print('修饰器1:begin')
fn()
print('修饰器1:end')
return wrapper
@decor2
@decor
def demo():
n = 10000
ret = 0
for n in range(n):
ret += n
print("result:", ret)
return
print(demo.__name__)
demo()
demo
修饰器2:begin
修饰器1:begin
result: 49995000
修饰器1:end
修饰器2:end
使用场景
现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。
装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated
日志(Logging)
日志是装饰器运用的另一个亮点。这是个例子:
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)
# Output: addition_func was called
使用装饰器的一些坑
1. 错误的函数签名和文档
装饰器装饰过的函数看上去名字没变,其实已经变了,包括doc。使用标准库里的functools.warps基本可以解决这个问题
def decorator(func):
@warps(func)
def warpper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
2. 不能用来装饰@classmethod 和 @staticmethod
因为@staticmethon和@classmethod返回的并不是一个callable对象,而是一个staticmethod的对象,这不符合装饰器的定义。解决方案是把装饰器放在@classmethod和@staticmethod之前。注意这里说的之前不是写在前一行,而是后一行,因为装饰器是从离函数最近的这个开始起作用的
@classmethod
@decorator
def func():
do_something