装饰器 decorator
标签: python
这里学习一下Python装饰器(Decorator)的用法。
首先要注意的是,Python中的函数可以像普通变量一样当做参数传递给另外一个函数,例如:
def foo():
print('foo')
def bar(func):
print('bar start')
func()
print('bar end')
bar(foo)
输出为:
bar start
foo
bar end
实际上装饰器本质上是python的一个函数或类,它可以在其他函数或类在不需要修改代码的前提下,附加某些额外功能。装饰器的返回值也是一个函数或类的对象。
它经常用于有切面需要的场景,比如:插入日志、性能测试、事务处理、缓存、教研权限等,装饰器是一个绝佳的选择。
我们可以抽离出与函数本身无关,但又在多个函数中存在需要的相同代码作为装饰器,并继续重用。
现在,假设我们要给foo()
增加功能,在执行函数时自动打印日志,又不修改原函数的内容。
def log(func):
def wrapper(*args, **kw):
print('%s() is running...' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def foo():
print('foo')
执行结果为:
>>> foo()
foo() is running...
foo1
像这样把@log
方法函数定义的开头,相当于执行了语句:
foo = log(foo)
由于log()
是一个装饰器,其返回一个函数。所以原来的foo()
函数依然存在,只是同名的函数指向了一个新的函数,于是调用foo()
将执行新的函数,即在log()
函数中返回的wrapper()
函数。
这里的wrapper()
函数只是作为一个原函数的代替。
wrapper()
函数的参数定义为(*args, **kw)
,因此,
wrapper()
函数可以接收任意参数的调用。在wrapper()
函数内,首先打印日志,然后执行原函数。
decorator需要传入参数
如果Decorator本身需要传入参数,那么就需要编写一个返回decorator的高阶函数。比如要自定义log的文本。
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s() %s' % (func.__name__, text))
return func(*args, **kw)
return wrapper
return decorator
@log('is runningggggg ......')
def foo(text):
print(text)
>>> foo('sdfsdfsdfsdf')
foo() is runningggggg ......
sdfsdfsdfsdf
三层嵌套的实际效果为:
foo = log(text)(foo)
返回函数的函数
log
返回decorator
,然后寻找decorator
的定义,将foo
作为参数传入,decorator
返回wrapper
,再寻找def wrapper()
,将foo
的参数传入,打印日志并返回foo(*args, **kw)
的结果。
我们来剖析上面的语句,首先执行log('execute')
,返回的是decorator
函数,再调用返回的函数,参数是foo
函数,返回值最终是wrapper
函数。
到这里,实际上foo
已经被换做为wrapper
了:
>>> foo.__name__
'wraper'
这样实际上我们还是修改了一部分内容,当需要用到函数名字作为依赖项时就会出现问题。
Python内置的functools.wraps
就可以解决这个问题,也就是执行了wrapper.__name__ = func.__name__
这样的代码。
一个完整的Decorator写法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def foo():
print('fooooo')
foo()
print(foo.__name__)
执行结果:
call foo():
fooooo
foo
带参数的log:
import functools
def log1(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s()' %(text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log1('ssssssssssss')
def foo1():
print('foo111111')
foo1()
foo1.__name__
结果:
ssssssssssss foo1()
foo111111
'foo1