1. 最基础的装饰器
- 装饰器基本的套路: 函数进,函数出. 函数名要作为参数传入装饰器,函数出: 装饰器要返回一个函数.
装饰器体现的是面向切面编程的思想(AOP), 和中间件一样.
from functools import wraps
def deco(func):
# 内层函数, 一般用来接收func的参数.
@wraps(func)
def inner(*args, **kwargs):
"""
this is a inner function
"""
# 执行func
print('decorating...')
result = func(*args, **kwargs)
return result
return inner
# @符是python为了实现装饰器而创造出来的语法糖.
@deco
def foo(x, y):
"""
return x ** y
"""
return x ** y
foo(2, 10)
# @ 这个语法糖做的事情,其实就是把func传入deco, 然后 再执行foo = deco(func)
# 相当于foo已经被deco装饰了
foo_ = deco(foo)(2, 10)
# 装饰器会改变原函数的两个东西 ,一个是原函数的__doc__, 另一个是__name__
# 通过 functools 导入一个wraps来解决装饰器修改原函数docstring和name
print(foo.__name__) # foo
print(foo.__doc__) # '\n return x ** y\n '
- 带参数的装饰器
带参数的装饰器, 就在最简单装饰器上再套一层函数,用来接收额外的参数.
from functools import wraps
def limit_output(max_value):
def deco(func):
# 内层函数, 一般用来接收func的参数.
@wraps(func)
def inner(*args, **kwargs):
"""
this is a inner function
"""
# 执行func
print('decorating...')
result = func(*args, **kwargs)
if result > max_value:
return '太大了,显示不了了'
return result
return inner
return deco
@limit_output(10000000000000000000000)
def foo(x,y):
"""
return x ** y
"""
return x ** y
foo(20,10)
out:decorating...
10240000000000
foo(20, 10000)
out:decorating...
'太大了,显示不了了'
2.类装饰器
想要实现类装饰器,必须让类实现
__call__
方法
class Deco:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# 执行func
result = self.func(*args, **kwargs)
print('Class Deco...')
return result
# 类装饰器
@Deco
def foo(x,y):
return x ** y
foo(2,3)
out:Class Deco...
8
- 带参数的类装饰器
带参数的类装饰器,是把参数从init中传入,函数是从call传入,call方法还需要有一层内部函数.
class Deco:
def __init__(self, max_value):
self.max_value = max_value
def __call__(self, func):
def inner(*args, **kwargs):
result = func(*args, **kwargs)
if result > self.max_value:
return '数字太大了.'
else:
return result
return inner
# 带参数的类装饰器
@Deco(10000000000000)
def foo(x,y):
return x ** y
foo(2,3)
out:8
foo(2,2000)
out:'数字太大了.'
3.多层装饰器
多层装饰器是从里往外执行的.
def deco1(func):
print('enter deco1...')
def inner1(*args, **kwargs):
print('enter inner1...')
return func(*args, **kwargs)
print('exiting deco1...')
return inner1
def deco2(func):
print('enter deco2...')
def inner2(*args, **kwargs):
print('enter inner2...')
return func(*args, **kwargs)
print('exiting deco2...')
return inner2
def deco3(func):
print('enter deco3...')
def inner3(*args, **kwargs):
print('enter inner3...')
return func(*args, **kwargs)
print('exiting deco3...')
return inner3
# 多层装饰器
@deco1
@deco2
@deco3
def foo(x,y):
return x ** y
foo(2,3)
enter deco3...
exiting deco3...
enter deco2...
exiting deco2...
enter deco1...
exiting deco1...
8
4.习题
- 练习1: 写一个 timer 装饰器, 计算出被装饰函数调用一次花多长时间, 并把时间打印出来
import time
from funfunctools import wraps
def timer(func):
@wraps(func) # 修正 docstring
def wrap(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(start - end)
return result
return wrap
- 练习2: 写一个 Retry 装饰器
import time
class retry(object):
def __init__(self, max_retries=3, wait=0, exceptions=(Exception,)):
self.max_retries = max_retries
self.exceptions = exceptions
self.wait = wait
def __call__(self, func):
def wrapper(*args, **kwargs):
for i in range(self.max_retries + 1):
try:
result = func(*args, **kwargs)
except self.exceptions:
time.sleep(self.wait)
continue
else:
return result
return wrapper