其他关于Python的总结文章请访问:https://www.jianshu.com/nb/47435944
详解Python中的装饰器(Decorator)-Python中的@符号详解
本质上,装饰器(decorator)就是一个能够返回函数的高阶函数,比如说我们有很多个函数A、B、C、D等等,我们希望有一个功能是在执行一个函数之前打印这个函数的名字,当然我们可以给每个函数的第一句手动加上打印名字的代码,或者我们在每次调用前都使用 print(function.__name__) 来打印它的名字,但是这显然是很麻烦的,装饰器提供了一种不需要更改原函数,但是可以动态地为函数增加功能的方式。原理很简单,我们可以创建一个返回函数的函数,在外层的函数中增加一个打印调用函数名称的代码,然后返回内部的函数。
简单的不带参数的装饰器
比如一个简单的函数:
def funcA():
print("hello world")
然后定义一个高阶函数:
def log(function):
def wrapper(*args,**kwargs):
print("Call {}".format(function.__name__))
return function(*args,**kwargs)
return wrapper
这样我们调用 log(funcA) 即达到我们的需求:
Call funcA
hello world
而装饰器提供了一个更简单的方式:使用@符号来完成:将 @decoratorName 放在希望使用装饰器来调用的函数前即可(使用装饰器的函数需要定义在装饰器的定义之后,即需要先定义 log 函数再定义带有 @log 装饰器的 funcA 函数:
@log
def funcA():
print("hello world")
这样再调用 funcA 就是调用使用了装饰器的 funcA,即现在 funcA = log(funcA)
带有参数的装饰器
现在考虑如果装饰器本身希望传入参数,那就需要一个返回decorator的高阶函数:
def log(text):
def decorator(function):
def wrapper(*args,**kwargs):
print("Call {} with {}".format(function.__name__,text))
return function(*args,**kwargs)
return wrapper
return decorator
@log('some text')
def funcA():
print("hello world")
funcA()
这样得到的结果就是:
Call funcA with some text
hello world
此时调用funcA实际上等价于调用了 log("some text")(funcA)
解决函数名字的错位问题
此时我们可以看到,funcA不再是原来的funcA,而实际是在log函数中返回的那个 wrapper 函数,所以如果我们检测此时的 funcA.__name__ 会发现并不是 'funcA' 而是 'wrapper',这其实是不好的,可以使用包 functools 中的使 functools.wraps 装饰器来解决,所以一个完整装饰器应该这样定义:
import functools
def log(function):
@functools.wraps(function)
def wrapper(*args,**kwargs):
print("Call {}".format(function.__name__))
return function(*args,**kwargs)
return wrapper
以及:
import functools
def log(text):
def decorator(function):
@functools.wraps(function)
def wrapper(*args,**kwargs):
print("Call {} with {}".format(function.__name__,text))
return function(*args,**kwargs)
return wrapper
return decorator








网友评论