1.装饰器原理
装饰器(Decorator),通过利用函数特性的闭包实现,能够动态为一个函数、方法或者类添加新的行为,而不需要通过子类继承或直接修改函数的代码来获取新的行为能力。
装饰器的作用就是为已经存在的对象添加额外的功能或者减少重复代码的使用。比如定义一个装饰器专门处理日志,日志处理完之后在执行真正的业务代码。
1.1 函数特性
1)函数作为变量传递,可以赋值
def sum(x,y):
return x+y
test = sum
test(1,2)
2)函数作为参数传递
def sum(x,y):
return x+y
def execute(func):
return func(1,2)
execute(sum)
3)函数作为返回值
def sum(x,y):
return x+y
def get_sum():
return sum
4)函数嵌套及跨域访问
def outer():
x = 1
def inner():
print(x)
inner()
outer()
1.2 闭包的实现
Python中的装饰器是通过闭包实现的,闭包就是引用了外部变量的内部函数,而闭包的实现正是利用了以上函数特性。
如下示例,函数func()被装饰成outerfun。
def func():
return '函数func'
def outer(func):
def inner(): # 函数嵌套
return 'inner中的 ' + func() # 跨域访问,引用了外部变量func
return inner # 函数作为返回值
outerfun = outer(func) # 函数func作为outer的参数;函数作为变量赋给outerfun
print(func()) # 执行原始函数
print(outerfun()) # 执行闭包
2.装饰器语法糖@
Python提供了语法糖@,执行outer(func) 只需把outer函数@到func()上即可。加了outer装饰器的func等价于outer(func)。
故@的作用:把被装饰的函数作为参数传递到装饰器函数里面加工,最后执行被装饰的函数相当于执行了一个加工后的函数。
def outer(func):
def inner():
return 'inner中的 ' + func()
return inner
@outer
def func():
return '函数func'
print(func())
3.装饰器种类
3.1函数装饰器
def decorator(func):
def wrapper(*wargs, **kwargs):
print('装饰器的世界你不懂')
return func(*wargs, **kwargs)
return wrapper
@decorator
def hello_world():
print('世界你好')
decorator(hello_world)() #显示调用装饰器
#print result
#装饰器的世界你不懂
#世界你好
3.2参数装饰器
def hello(params):
def decorator(func):
def wrapper(*wargs, **kwargs):
print(params)
print(wargs)
return func(*wargs, **kwargs)
return wrapper
return decorator
@hello('装饰器的世界你不懂')
def hello_world(value):
print('世界你好')
hello('装饰器的世界你不懂')(hello_world)('hello') #显示调用带有参数的装饰器
#print result
#装饰器的世界你不懂
#('hello',)
#世界你好
3.3 wraps装饰器
一个函数不止有执行语句,还有 name(函数名),doc (说明文档)等属性,之前的例子会导致这些属性改变。
def decorator(func):
def wrapper(*wargs, **kwargs):
"""doc of wrapper"""
print('装饰器的世界你不懂')
return func(*wargs, **kwargs)
return wrapper
@decorator
def hello_world():
"""doc of hello_world"""
print('世界你好')
print(hello_world__name__) #wrapper
print(hello_world.__doc__) #doc of wrapper
#print result
#装饰器的世界你不懂
#世界你好
装饰器返回的wrapper函数替换了hello_world,导致函数名和帮助文档变成了wrapper函数的。functools模块下的wraps装饰器就是解决这一问题。
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*wargs, **kwargs):
"""doc of wrapper"""
print('装饰器的世界你不懂')
return func(*wargs, **kwargs)
return wrapper
@decorator
def hello_world():
"""doc of hello_world"""
print('世界你好')
print(hello_world__name__) #hello_world
print(hello_world.__doc__) #doc of hello_world
#print result
#装饰器的世界你不懂
#世界你好
3.4 类装饰器
类能实现装饰器的功能, 是由于当我们调用一个对象时,实际上调用的是它的 call 方法。
class Decorator:
def __init__(self, func):
self.func = func
def __call__(self, *wargs, **kwargs):
print('装饰器的世界你不懂')
return self.func(*wargs, **kwargs)
@Decorator
def hello_world():
print('世界你好')
hello_world()
#print result
#装饰器的世界你不懂
#世界你好
因为通过类我们可以将执行过程拆解到各函数中,降低代码的复杂度,甚至可以通过类属性实现一些复杂功能。
3.5 内置装饰器
有三种经常会用到的装饰器, property、 staticmethod、 classmethod,他们共同点都是作用于类方法之上。
1)property 装饰器用于类中的函数,可以像访问属性一样获取一个函数的返回值,又可以在获取值的同时做一些操作。
class XiaoMing:
first_name = '明'
last_name = '小'
@property
def full_name(self):
return self.last_name + self.first_name
xiaoming = XiaoMing()
print(xiaoming.full_name)
#print result
#小明
2)staticmethod 装饰器同样是用于类中的方法,表示这个方法是一个静态方法,该方法可以直接被调用无需实例化,但同样它没有 self 参数,也无法访问实例化后的对象。
class XiaoMing:
@staticmethod
def hello_world():
print('世界你好')
XiaoMing.hello_world() #世界你好
#实例化调用也是同样的效果
xiaoming = XiaoMing()
xiaoming.hello_world() #世界你好
3)classmethod 装饰器同样是用于类中的方法,表示这个方法是一个类方法,该方法可以直接被调用无需实例化,但同样它没有self参数,也无法访问实例化后的对象。相对于 staticmethod 的区别在于它会接收一个指向类本身的cls参数。
class XiaoMing:
name = '小明'
@classmethod
def hello_world(cls):
print('世界你好,'+cls.name+'来啦')
XiaoMing.hello_world()
#世界你好,小明来啦
#<class '__main__.XiaoMing'>
最后,附参考的知乎链接:https://www.zhihu.com/question/26930016/answer/1904166977