文章先由stackoverflow上面的一个问题引起吧,如果使用如下的代码:
@makebold
@makeitalic
def say():
return "Hello"
打印出如下的输出:
<b><i>Hello<i></b>
你会怎么做?最后给出的答案是:
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## 返回 <b><i>hello world</i></b>
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。
概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
一、装饰器: 本质就是函数,为其他函数添加附加功能
原则:
1. 不修改被修饰函数的源代码
2. 不修改被修饰函数的调用方法
二、装饰器:高阶函数 + 函数嵌套 + 闭包
1. 高阶函数
定义:
函数接受的参数是一个函数名
函数的返回值是一个函数
满足上述任意一个,都可以称之为高阶函数
def timmer(func):
def warpper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('程序运行时间%s'%(end_time - start_time))
return res
return warpper
函数 timmer 返回的 warpper 便是一个函数名; timmer 传入的参数 func 也可以 是个函数名
2. 函数嵌套
定义:
在函数中定义另外一个函数
def timmer(func):
def warpper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('程序运行时间%s'%(end_time - start_time))
return res
return warpper
依然是上面例子: warpper 函数定义在 timmer 函数内部
3. 闭包
定义:
闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。而引用环境由LEGB导致的一种现象。
进一步解释就是:
在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。
闭包是装饰器涉及到的高级语法中最难理解的一个。
四、装饰器分类
1. 不带参数的装饰器
import time
def timmer(func):
def warpper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('程序运行时间%s'%(end_time - start_time))
return res
return warpper
@timmer
def test(name,num):
time.sleep(1)
print('the country is %s the nation number %d'%(name,num))
return 123
@timmer 等价于:
@timmer <==> test = timmer(test)
2. 带参装饰器
示例一:
def log(prefix):
def log_decorator(f):
def wrapper(*args, **kw):
print '[%s] %s()...' % (prefix, f.__name__)
return f(*args, **kw)
return wrapper
return log_decorator
@log('DEBUG')
def test():
pass
@log('DEBUG')等价于
my_func = log('DEBUG')(my_func)
或者
log_decorator = log('DEBUG')
my_func = log_decorator(my_func)
示例二:
class Flask(_PackageBoundObject)
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if request.method == 'POST' and form.validate():
with db.auto_commit():
user = User()
user.set_attrs(form.data)
db.session.add(user)
return redirect(url_for('web.login'))
return render_template('auth/register.html', form=form)
python Flask 框架中路由装饰器便是一个带参的装饰器。
@web.route('/register', methods=['GET', 'POST'])等价于:
register = route('/register', methods=['GET', 'POST')(register)
即
register = route('/register', methods=['GET', 'POST') 返回 decoretor <==> register = decoretor
register = decoretor(register) 返回 register 。
route路由器返回的依然是原函数,只是在装饰器中将被装饰的函数,加入到路由map中( self.add_url_rule(rule, endpoint, f, **options))。
3. 多层装饰器
情况1:
def A(funE_decorated_by_C):
def redecorated_E(str):
return funE_decorated_by_C(str)+' > redecorated by A'
return redecorated_E
def C(funE):
def decorated_E(str):
return funE(str)+' > decorated by C'
return decorated_E
@A
@C
def E(str):
return str
print(E('A string is '))
这种情况下,有 E(str) = A(C(E))(str)。首先装饰器 C 装饰函数 E,返回一个被 C 装饰过的函数,然后装饰器 A 再装饰这个被 C 装饰过的函数。与第二种情况的区别是,这里的装饰器 A 装饰的是一个函数,而不是一个装饰器。
情况2:
def A(funC):
def decorated_C(funE):
def decorated_E_by_CA(*args, **kwargs):
out = funC(funE)(*args, **kwargs)
return out +' > decorated by A'
return decorated_E_by_CA
return decorated_C
@A
def C(funE):
def decorated_E_by_C(str):
return funE(str)+' > decorated by C'
return decorated_E_by_C
@C
def E(str):
return str
print(E('A string is '))
这种情况下首先 E(str) = C(E)(str),然后由于C = A(C),还有 E(str) = A(C)(E)(str)。这么一来他们的关系就明确了,装饰器 A 装饰的是装饰器 C,它返回了一个被装饰过的装饰器,而被装饰过的装饰器又可以去装饰函数 E。在上面的代码中,decorated_C 就是一个被装饰过的装饰器。
参考博客: