python中的装饰器其实是闭包这种思路的一种应用
闭包
在函数中再嵌套一个函数,并且引用外部函数的变量,这就是一个闭包了。
案例:
def outer(x):
def inner(y):
return x+y;
return inner
print(outer(1)(2)) # 3
说明:调用outer函数,传入参数1,返回一个函数inner,然后调用inner函数,传入参数2,返回结果。
简单装饰器
装饰器本质上就是闭包的一种应用
装饰器其实就是传进去一个函数,然后在调用该函数之前,做一些其它事情,然后再返回新的函数。其实本质上就是在不影响原来函数功能的基础上为其添加新的功能。
案例:
def debug(func):
def wrapper():
print(f'[DEBUG]: Test Bug {func.__name__}') # 新的功能
return func() # 调用原函数,并返回原函数的返回值
return wrapper # 返回新的函数
@debug
def hello():
print('Hello world!')
return 1
print(hello())
"""
[DEBUG]: Test Bug hello
Hello world!
1
"""
说明:使用debug函数作为装饰器,将函数hello传入到debug中,返回一个经过处理的新函数wrapper,调用函数wrapper(),执行添加的代码后,调用原函数并返回原函数的返回值。
带参数的装饰器
其实就是再向外添加一层函数,最外层函数的参数用于装饰器传参
案例:
def debug(pattern):
def out_wrapper(func):
def wrapper():
print(f'[{pattern}]: Test Debug')
return func()
return wrapper
return out_wrapper
@debug(pattern='DEBUG')
def hello():
print('Hello world!')
return 1
print(hello())
"""
[DEBUG]: Test Debug
Hello world!
1
"""
说明:使用debug函数作为装饰器,先传入装饰器参数到最外层函数,返回一个新的函数out_wrapper,out_wrapper接收原函数hello作为参数,返回一个新的函数wrapper,调用函数wrapper,先执行新添加的代码,然后执行原函数并返回原函数的返回值。
类装饰器
装饰器也不一定只能用函数来写,也可以使用类装饰器,用法与函数装饰器并没有太大区别,实质是使用了类方法中的魔法方法__call__来实现类的直接调用。
不带参数的类装饰器
class debug(object):
def __init__(self,func):
self.func = func
def __call__(self, *args, **kwargs):
print('[DEBUG]: Test Debug')
return self.func(*args, **kwargs)
@debug
def hello():
print('Hello world!')
return 1
print(hello())
"""
[DEBUG]: Test Debug
Hello world!
1
"""
说明:
该类的初始化方法接收hello函数作为参数进行初始化,然后当该函数被调用时,调用该类的__call__方法,原函数的参数被__call__方法的参数接收,执行完新添加的代码后,执行原函数并返回原函数的返回值。
__call__是 Python 中的魔法方法,用于将一个类的实例像函数一样进行调用。当一个对象被调用时,Python 会自动调用该对象的__call__方法,所以当加上装饰器后,其实可以把原函数看成是该类的实例化对象。
带参数的类装饰器
class debug(object):
def __init__(self,pattern):
self.pattern = pattern
def __call__(self, func):
def wrapper(*args, **kwargs):
print(f'[{self.pattern}]: Test Debug')
return func(*args, **kwargs)
return wrapper
@debug('DEBUG')
def hello():
print('Hello world!')
return 1
print(hello())
"""
[DEBUG]: Test Debug
Hello world!
1
"""
说明:装饰器参数被该类的初始化方法接收,并初始化作为属性,当被修饰函数hello被调用时,调用__call__方法,接收原函数作为参数,返回一个新的函数wrapper,wrapper接收原函数的参数,执行完新添加的代码后,执行原函数并返回原函数的返回值。