原创转载请注明出处:http://agilestyle.iteye.com/blog/2330385
通过__name__属性获取函数的名字
def hello_world():
print('Hello World')
f = hello_world
# hello_world
print(hello_world.__name__)
# hello_world
print(f.__name__)
接下来,我们为hello_world函数做一个增强,但是并不改变原函数的定义,而是在代码运行期间动态增加额外的功能,在Python中成为装饰器(Decorator),就好比Spring的AOP。
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def hello_world():
print('Hello World')
hello_world()
# wrapper
print(hello_world.__name__)
Console Output
Note:
由于log()是一个decorator,返回一个函数,所以原来的hello_world()函数仍然存在,于是调用hello_world()将执行新函数,即在log()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
decorator本身需要传入参数,比如自定义log的文本
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s()' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('invoke')
def hello_world():
print('Hello World')
hello_world()
# wrapper
print(hello_world.__name__)
Console Output
Note:
以上两种decorator的定义都没有问题,可函数也是对象,有__name__等属性,但是经过decorator装饰之后的函数,它们的__name__已经从原来的'hello_world'变成了'wrapper',因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
可以编写这样的代码解决问题:
wrapper.__name__ = func.__name__
但是并不需要,Python内置的functools.wraps就是干这个事的,所以一个完整的decorator的写法如下:
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def hello_world():
print('Hello World')
hello_world()
# hello_world
print(hello_world.__name__)
Console Output
针对带参数的decorator
from functools import wraps
def log(text):
def decorator(func):
@wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('invoke')
def hello_world():
print('Hello World')
hello_world()
# hello_world
print(hello_world.__name__)
Console Output