装饰器(上)
python 的装饰器属于函数式编程的范畴。维基百科可以点击链接看看维基百科上的解释。没错,我看得一脸懵逼。就目前学习的而言,我把它总结为面向函数的编程。这样就好理解一下,当然实际上肯定有所偏颇。在面向过程中,函数是组成过程的单位。面对对象的基石是对象。而函数式这种范式下函数是基石,当做变量的存在。
装饰器的初衷
首先装饰这个词可以理解一下。装饰不就是在原有的物品上加以修饰,添加一些看起来不是特别重要的美化功能。那么对应于编程来说,装饰器就是修饰原本函数用的一种方法了。装饰器模式不改变原有的业务逻辑,对代码没有侵略性。所以常常可以用来做日志统计,异常捕获,web的权限校验等操作。
装饰器示例
我们想要在函数运行前打印日志会这么做:
原函数:
def now():
print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
装饰器:
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
装饰器修饰原函数语法:
@log
def now():
print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
@log 是什么鬼呢?其实他就是个语法糖 概念点击链接。
相当于执行了
now = log(now)
那么这时候的now是什么?参看上面那就是return的 wrapper。
import time
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
def now():
print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if __name__ == '__main__':
f = log(now())
print f
f()
输出:
<function wrapper at 0x1042232a8>
call now():
2018-08-03 09:42:19
Process finished with exit code 0
可以看到返回的f就是wrapper。如果我们这个时候调用和装饰器是一样的。注意在使用装饰器的时候他已经执行完了赋值返回wrapper这一段。所以如果是在:
def log(func):
# do something before function call ---1
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
上面的do something 会在调用前执行。
至此我们所认识的最简单的装饰器就完成了。
函数带参数
函数原本所传的参数需要和返回的wrapper一致。这个容易理解,因为实际在调用的时候实际上是调用wrapper。这里传参是也有两种写法,一种是对应上,log函数,一种是使用省略语法,可以兼容所有的函数。
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
def log_1(func):
def wrapper(a, b, c):
print 'call %s():' % func.__name__
return func(a, b, c)
return wrapper
@log
def add(a, b, c):
print a + b + c
@log_1
def add_1(a, b, c):
print a + b + c
if __name__ == '__main__':
add(1, 2, 3)
add_1(1, 2, 3)
除了这种使用我们还可以在wrapper中新增加参数传入到函数中。
def add_one_param(func):
def wrapper(a, b, c):
print 'call %s():' % func.__name__
d = 100
return func(a, b, c, d)
return wrapper
@add_one_param
def add_one_more_param(a, b, c, d):
print a + b + c + d
if __name__ == '__main__':
add_one_more_param(1, 2, 3)
输出:
call add_one_more_param():
106
如果把参数改为如下也是没毛病的:
def add_one_param(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
d = 100
return func(d, *args, **kw)
return wrapper
装饰器带参数
装饰器带参数一般而言是用于区分装饰器的功能。比如一个装饰器是日志功能。日志有等级之分,则可以在装饰器上传入参数区分打印。
语法
需要在原本装饰器外在包装一层:
def log(log_level):
def decorator(func):
def wrapper(*args, **kw):
if log_level == WARNING_LEVEL:
print WARNING_LEVEL
elif log_level == ERROR_LEVEL:
print ERROR_LEVEL
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
return decorator
@log(ERROR_LEVEL)
def add(a, b, c):
print a + b + c
if __name__ == '__main__':
add(1, 2, 3)
输出:
error
call add():
6
当这样调用的时候,很明显参数已经传入了。