文章目录
1. 前言
装饰器是python中的一种特殊语法,装饰器是在闭包的基础上实现的。
像python中的functools包是python中高级函数的用法,其中有wrapper,update_wrapper,particial等其实都是装饰器,只不过各有各的作用。
装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
上文来自 Python 装饰器
简单讲一下装饰器
@decorator
def test():
pass
#实际等价于
test = decorator(test)
带参数的装饰器
# 带参数的装饰器
@decorator(a, b)
def test():
pass
#实际等价于
test = decorator(a, b)(test)
带参数的装饰器就是,就是用执行了该装饰器函数之后所返回的函数再来装饰原函数。
2. 装饰器的实现
装饰器可以通过函数实现或类实现。
2.1 函数实现装饰器
from functools import wraps
def decorator(func):
print("装饰时调用此句,原函数name是{}".format(func.__name__))
@wraps(func)
def inner(*args, **kwargs):
print("这是装饰器添加的日志")
return func(*args, **kwargs)
return inner
@decorator
def test(name, age=None, gender=None):
print('name is {}, age is , gender is {}'.format(name, age, gender))
print('test函数的name是{}'.format(test.__name__))
if __name__ == '__main__':
test('tom', 18, 'boy')
pass
输出结果如下:
# 输出,使用wraps之前
装饰时调用此句,原函数name是test
这是装饰器添加的日志
name is tom, age is 18, gender is boy
test函数的name是inner
# 输出,使用wraps之后
装饰时调用此句,原函数name是test
这是装饰器添加的日志
name is tom, age is 18, gender is boy
test函数的name是test
使用functools中的wraps是因为,直接通过函数定义装饰器之后,会改变原函数的一些属性,例如,__name__
属性等。
有一点需要注意的是,哪些是在调用了被装饰函数时才执行的,哪些是在装饰的时候就已经执行的。
当注释了test(‘tom’, 18, ‘boy’)之后,发现仍然会打印 “装饰时调用此句,原函数name是test”
2.2 类实现装饰器
class Decorator(object):
def __init__(self, func):
print("这是在使用装饰器的时候,{}".format(func.__name__))
self.func = func
def __call__(self, *args, **kwargs):
print("这是装饰器添加的日志")
return self.func(*args, **kwargs)
@Decorator
def test(name, age=None, gender=None):
print("name is {}, age is {}, gender is {}".format(name, age, gender))
#print('test函数的name是{}'.format(test.__name__))
if __name__ == '__main__':
#pass
test('tom', 18, 'boy')
输出结果如下:
# 输出
装饰时调用此句,原函数name是test
这是装饰器添加的日志
name is tom, age is 18, gender is boy
在装饰 test函数时,实际上时创建了一个Decorator的实例对象,当调用test(‘tom’, 18, ‘boy’)时,其实就是调用了这个实例对象,调用实例对象实际就是调用的类中的__call__
方法。
需要注意的是,如果要用类来实现带参数的装饰器,那么__call__方法必须成为一个装饰器(在__call__中还需要定义一个函数)
。例如,
class Decorator(object):
def __init__(self, a, b):
print("这是在使用装饰器的时候,{}".format(func.__name__))
self.a = a
self.b = b
def __call__(self, func):
print("这也是在使用装饰器的时候就执行的")
def inner(*args, **args):
print("这是装饰器添加的日志")
return self.func(*args, **kwargs)
return inner
@Decorator(a, b)
def test(*args, **kwargs):
pass
3. 装饰器的使用
3.1 装饰函数
3.1.1 函数装饰函数
2.1中就是函数装饰函数的例子。
3.1.2 类装饰函数
2.2中就是类装饰函数的例子。
3.2 装饰类
3.2.1 函数装饰类
from functools import wraps
def decorator(cls):
print('装饰类时调用')
@wraps
def inner(*args, **kwargs):
print('这是添加日志的地方{}'.format(cls.__name__))
return cls(*args, **kwargs) # 此时是创建类的实例
return inner
@decorator
class Test(object):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def run(self):
print('这是run方法')
3.2.2 类装饰类
class Decorator(object):
def __init__(self, cls):
print("这是在使用装饰器的时候,{}".format(cls.__name__))
self.cls = cls
def __call__(self, *args, **kwargs):
print("这是装饰器添加的日志")
return self.cls(*args, **kwargs)
@Decorator
class Test(object):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def run(self):
print('这是run方法')
4. 总结
函数之所以可以装饰,是将函数的引用传递给了装饰器函数。装饰器函数执行某些语句之后才执行真正的传递进来的原函数。
通过画图可以看出再装饰器装饰过程种的引用变化,可以帮助理解装饰器。
5. 参考文献
[1] Python 装饰器
[2] python装饰器的4种类型:函数装饰函数、函数装饰类、类装饰函数、类装饰类
[3] Python各种类型装饰器详解说明