python装饰器
装饰器的作用就是在不修改函数的情况下为已存在函数添加额外功能。
举个例子,有两个函数test1和test2,我需要记录我每次执行的是哪个函数。下面是我一般的做法。
def test1():
print "enter test1"
print "hello"
def test2():
print "enter test2"
print "world"
我们只是打印一下函数名,如果是更复杂一点的功能呢,这个就造成代码重复了
def test1():
print “hello”
def test2():
print “world”
def logger(func):
print “enter %s” % func.__name__
func()
logger(test1)
logger(test2)
这个方法比上个好一点,利用了函数名作为实参(实参高阶函数)但是却改变了函数的调用方式。
下面这个方法是利用了返回值高阶函数,返回值中含有函数名
def test1():
print “hello”
def test2():
print “world”
def logger(func):
print “enter %s” % func.__name__
return func
t1 = logger(test1)
t1()
t2 = logger(test2)
t2()
那么怎么写一个装饰器呢?根据上面的例子我们知道,函数名可以作为参数,并且可以作为返回值。装饰器其实是一个闭包,把函数作为参数并且返回一个替代函数。如下所示:
def logger(func):
def wrapper()
print "enter %s" % func.__name__
return func()
return wrapper
test1 = logger(test1)
test2 = logger(test2)
test1()
test2()
python version < 2.4时就是这样为函数增加额外的功能的。
这个其实就是装饰器,他对原函数进行包装,增加了额外的功能,并且返回了另一个函数。
后面版本的python支持了@语法糖。
'@'符号用作函数修饰符是python2.4新增加的功能,修饰符必须出现在函数定义前一行,不允许和函数定义在同一行。也就是说@A def f(): 是非法的。 只可以在模块或类定义层内对函数进行修饰,不允许修修饰一个类。一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。
2.4之后的版本有了语法糖,我们就可以这样写装饰器了。
def logger(func):
def wrapper()
print "enter %s" % func.__name__
return func()
return wrapper
@logger
def test1():
print “hello”
@logger
def test2():
print “world”
上面是一个简单的装饰器。
如果函数有参数,而添加的额外功能需要用到参数,我们可以用下面的方法:
def logger(func):
def wrapper(*args, **kwargs):
print "enter %s" % func.__name__
print "Arguments is %s, %s" % (args, kwargs)
return func(*args, **kwargs)
return wrapper
@logger
def test1(x, y=3):
print "%s * %s = %s" % (x, y, x*y)
@logger
def test2(x, y):
print "%s + %s = %s" %(x, y, x+y)
@logger
def test3(**kwargs):
print "kwargs: %s" % kwargs
如果同一个装饰器需要对不同函数有不同处理,那么就需要装饰器带参数, 用参数标记一下。例如我们代码里面的role_required装饰器。下面是个简单的例子:
def logger(parameter):
print "Parameter is: %s" % parameter
def outer(func):
print "outer wrapper"
def wrapper(*args, **kwargs):
print "enter %s" % func.__name__
print "Arguments is %s, %s" % (args, kwargs)
return func(*args, **kwargs)
return wrapper
return outer
@logger(parameter='test')
def test(x, y=3):
print "%s * %s = %s" % (x, y, x*y)
print "================start===================="
test(2, 4)
运行结果如下:
Parameter is: test
outer wrapper
================start====================
enter test
Arguments is (2, 4), {}
2 * 4 = 8
首先logger(parameter)带参数,接收参数parameter,而@logger(parameter='test')刚好也是带一个参数的,就会执行这个函数。相当于:
logger = logger(parameter)
test = logger(test)