其他关于Python的总结文章请访问:https://blog.csdn.net/qq_38962621/category_10299380.html
详解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