接下来要来谈谈 functools.wraps 的功用,虽然使用装饰器可以大大的减少重复的 code,但是他有一个缺点,就是你会发现 f1 function 中的 name 和 doc 结果会怪怪的,以下的例子。
代码语言:javascript复制def my_logging(func):
def wrapper(*args, **kwargs):
"""my wrapper"""
print('logging - {} is running'.format(func.__name__))
func(*args, **kwargs)
return wrapper
@my_logging
def f1(*args, **kwargs):
"""f1 function"""
print("f1")
for thing in args:
print('hello {}'.format(thing))
for name, value in kwargs.items():
print('{0} = {1}'.format(name, value))
f1('twtrubiks', apple='fruit', cabbage='vegetable')
print('f1.__name__', f1.__name__) # output -> 'wrapper'
print('f1.__doc__', f1.__doc__) # output -> 'my wrapper'
将 f1.__name__ 以及 f1.__doc__ 印出来,竟然出现了 wrapper 的东西,( 也就是说,f1 被 wrapper 取代了,所以才显示 wrapper 的信息 ),所以为了解决这个问题,可以使用 functools.wraps 这个装饰器,将原本函数里面的信息复制到 func 之中,这样就会让 func 也有原函数 f1 的信息,方法如下。
代码语言:javascript复制from functools import wraps
def my_logging(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""my wrapper"""
print('logging - {} is running'.format(func.__name__))
func(*args, **kwargs)
return wrapper
@my_logging
def f1(*args, **kwargs):
"""f1 function"""
print("f1")
for thing in args:
print('hello {}'.format(thing))
for name, value in kwargs.items():
print('{0} = {1}'.format(name, value))
f1('twtrubiks', apple='fruit', cabbage='vegetable')
print('f1.__name__', f1.__name__) # output -> 'f1'
print('f1.__doc__', f1.__doc__) # output -> 'f1 function'
这样子就可以正确的显示 f1 的资讯了。装饰器除了装饰 function 之外,也可以装饰 class,class decorator 主要是依赖 __call__ 的方法。
代码语言:javascript复制class MyDecorator:
def __init__(self, param):
self.__param = param
def __call__(self, func):
def wrapper(*args, **kwargs):
print('do something before calling function {}'.format(func.__name__))
print('self.__param', self.__param)
func(*args, **kwargs)
print('do something after calling function {}'.format(func.__name__))
return wrapper
@MyDecorator('level')
def f1(*args, **kwargs):
print('f1')
for thing in args:
print('hello {}'.format(thing))
for name, value in kwargs.items():
print('{0} = {1}'.format(name, value))
f1('twtrubiks', apple='fruit', cabbage='vegetable')
看起来稍微比较复杂,但其实就是 closures 闭包的概念。
装饰器还有一个神奇的功能,就是用装饰器来注册函数,先来看一个 flask 框架中常常用来注册 url 的方法,相信如果写过 flask 的人,一定对下面这段 code 不陌生。
代码语言:javascript复制@app.route("/")
def index():
pass
但是你有想过,他是怎麽办到的吗❓其实就是利用了我刚刚说的另外一个特性,用装饰器来注册函数。不太懂意思,没关系,来看一个例子。
代码语言:javascript复制registry = dict()
def route(rule):
def decorator(f):
registry[rule] = f # <1>
return f
return decorator
@route('/')
def index():
print('hello')
return 'hello'
index()
print('registry:', registry) # <2>
重点在 <1> 的部分,我们只是将 rule 以及 f 注册到 registry,然后再 return 原本的 function ( 没经过任何加工,只单纯注册 url ),如果你观察 <2> 的输出,你也会发现 registry 里面有 url 的 mapping。所以装饰器除了装饰 ( 扩充 ) 原本的功能之外,还有注册函数的功用。