@magine
2015-02-02T11:37:22.000000Z
字数 2083
阅读 1252
实习日记
装饰器
装饰器是一种设计模式,它可以将对象或函数的真正功能与其他辅助的功能分离开。
一个简单的装饰器模样如下:
def decorator(f):
# before decorate function
def decorated_f(*args, **kwargs):
# do something with args, kwargs and the function f
return f(*args, **kwargs)
# after decorate function
return decorated_f
装饰器本身就是函数,所以你可以直接用其返回值覆盖原有的函数。
def func(*args, **kwargs):
# do something
func = decorator(func)
此处使用这种写法是为了便于大家理解装饰器的工作原理(如果你能读懂上面两个代码片段,就不要浪费时间看下面那段废话和这句废话):
func
这个函数被作为参数传入到decorator
函数中,该函数内部定义了一个函数decorated_f
,decorated_f
在对传入的参数(即args
和kwargs
)以及func
函数做一些处理后,把处理后的参数传递给func
,并返回它的执行结果(注意,是执行结果,不是函数本体)。
CAUTION: 以上对于装饰器的定义,注意以下两点:
@decorator
def func(*args, **kwargs):
# do something
这种写法与上一种完全等价,但是你应该尽量使用这一种,因为Python之禅说过:
Beautiful is better than ugly.
补充一点,装饰器可以叠加使用:
@decorator3
@decorator2
@decorator1
def func(*args, **kwargs):
# do something
编号越小(即越靠近函数),越先被调用。
诚如上文所见,一个标准的装饰器是不能传入参数的。如果想要装饰器更灵活一点,你可以再次为它加上一层“包装”(它本身就包装了函数,不是么?),或者说你需要调用的是一个生成装饰器的函数(decorator factory)。
就像这个样子:
# 注意我在第2行与第4行的独特传参方式,当然你不一定非要这样写
def factory(*args, **kwargs):
def decorator(f):
def decorated_f(*args, **kwargs):
# do something with args, kwargs and the function f
return f(*args, **kwargs)
return decorated_f
return decorator
@factory(*args, **kwargs)
def func(*args, **kwargs):
# do something
那么去掉语法糖应该写成什么样呢?嗯,它看起来有点儿古怪:
def func(*args, **kwargs):
# do something
func = factory(*args, **kwargs)(func)
我想无需多说,你已经可以自己判断出哪些部分是在装饰时运行,哪些部分是在函数调用时运行。
调用经过装饰的函数,相当于调用一个新函数,那么查看函数参数、注释、甚至函数名的时候,就只能看到装饰器的相关信息,而被包装函数的信息则被丢掉了。
别担心,wraps()
可以帮你转移这些信息。
from functools import wraps
# 不含参数的
def decorator(f):
@wraps(f)
def decorated_f(*args, **kwargs):
# do something with args, kwargs and the function f
return f(*args, **kwargs)
return decorated_f
# 含参数的
def factory(*args, **kwargs):
def decorator(f):
@wraps(f)
def decorated_f(*args, **kwargs):
# do something with args, kwargs and the function f
return f(*args, **kwargs)
return decorated_f
return decorator
functools.wraps()
的工作原理,想知道更多吗?