【摘要】
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.;常用场景比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的代码并重用。本章主要讲解几种装饰器的写法,包括被装饰函数是否含参数、装饰器是否带参数、类装饰器。
- 普通装饰器不带参数,被装饰函数不带参数
- def dec1(func):
- print('========2===========')
- def inner():
- print('========3===========')
- func()
- return inner
- @dec1 # 只要Python解释器解释到这一行时,就会自动执行装饰,而不是等到要执行f1()函数才开始装饰 func1=dec1(func1)
- def func1():
- print('========1===========')
- func1()
- # 输出如下:
- # ========2===========
- # ========3===========
- # ========1===========
- 普通装饰器不带参数,被装饰函数带参数
- # 由于被装饰后的函数返回的是装饰器的内函数,因此,只需要往装饰器的内函数加上可变参数*args和关键字参数**kwargs,
- def dec2(func):
- print('========2===========')
- def inner(*args, **kwargs):
- print('========3===========')
- func(*args, **kwargs)
- return inner
- @dec2 # @dec2等同于func2 = dec2(func2)('hello','world')
- def func2(a, b):
- print('========1===========')
- print(a, b)
- func2('hello', 'world')
- # 输出如下:
- # ========2===========
- # ========3===========
- # ========1===========
- # ('hello', 'world')
- 普通装饰器带参数,被装饰函数也带参数
- # 外层嵌套一层函数传参,返回一个装饰器,这时就可以通过外层的函数传进参数
- def outer(arg):
- print arg
- def dec3(func):
- print('========2===========')
- def inner(*args, **kwargs):
- print('========3===========')
- func(*args, **kwargs)
- return inner
- return dec3
- @outer('带参数装饰器') # 等同于func3 = outer('带参数装饰器')(func3)('hello','world')
- def func3(a, b):
- print('========1===========')
- print(a, b)
- func3('hello', 'world')
- # 输出如下:
- # 带参数装饰器
- # ========2===========
- # ========3===========
- # ========1===========
- # ('hello', 'world')
- 类装饰器(类中重写__call__方法)不带参数,被装饰函数也不带参数
- class Decorator1(object):
- def __init__(self, func):
- self.func = func
- def __call__(self):
- print '这是不接受参数的类装饰器'
- self.func()
- @Decorator1
- def func6():
- print('========1===========')
- func6() # 相当于执行类中的__call__方法
- # 输出如下:
- # 这是不接受参数的类装饰器
- # ========1===========
- 类装饰器不带参数,被装饰函数带参数
类装饰器需要满足两个条件:1)类的实例是可以调用的,可通过实现函数__call__,从而使得类的实例可以被调用;2)需要将被装饰函数传递到类的实例里,可通过定义__init__时将被修饰函数传递进去。
- class Decorator2(object):
- def __init__(self, func):
- self.func = func
- def __call__(self, *args, **kwargs):
- print '这是接受参数的类装饰器'
- self.func(*args, **kwargs)
- @Decorator2
- def func7(a, b):
- print('========1===========')
- print(a, b)
- func7('hello', 'world')
- # 输出如下:
- # 这是接受参数的类装饰器
- # ========1===========
- # ('hello', 'world')
- 类装饰器带参数,且被装饰对象也带参数
- class Decorator3(object):
- def __init__(self, x, y):
- self.x = x
- self.y = y
- def __call__(self, func, *args, **kwargs):
- print '这是接受参数的类装饰器,而且装饰器也带参数'
- print(self.x, self.y)
- def wrapper(*args, **kwargs):
- func(*args, **kwargs)
- return wrapper
- @Decorator3('d1', 'd2')
- def func8(a, b):
- print('========1===========')
- print(a, b)
- func8('hello', 'world')
- # 输出如下:
- # 这是接受参数的类装饰器,而且装饰器也带参数
- # ('d1', 'd2')
- # ========1===========
- # ('hello', 'world')
- 使用@functools.wraps的目的
- def dec4_nowraps(func):
- print('========2===========')
- def inner():
- print('========3===========')
- func()
- return inner
- def dec5_withwraps(func):
- print('========2===========')
- @functools.wraps(func)
- def inner():
- print('========3===========')
- func()
- return inner
- @dec4_nowraps
- def func4():
- print('========1===========')
- print ('函数名为{0}'.format(func4.__name__))
- @dec5_withwraps
- def func5():
- print('========1===========')
- print ('函数名为{0}'.format(func5.__name__))
- func4()
- # 输出如下,函数名为装饰器的内函数名:
- # ========2===========
- # ========3===========
- # ========1===========
- # 函数名为inner
- func5()
- # 输出如下,函数名为被装饰的函数名:
- # ========2===========
- # ========3===========
- # ========1===========
- # 函数名为func5
- 总结
以上便是对装饰器几种写法的介绍,工作中可以根据自身实际情况,灵活运用。