装饰器
什么是装饰器?
软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。
’装饰’代指为被装饰对象添加新的功能,’器’代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。
装饰器是 Python 中一种强大的编程模式,允许你通过在不修改原始函数代码的情况下,动态地扩展或修改函数的行为。
是不是觉得并不那么好理解,没关系,可以看看下面这个形象的比喻。
想象你开了一家咖啡馆。你有一道招牌咖啡,但你发现顾客们对它的味道想要做一些定制。而你又不想改变招牌咖啡的原始配方,因为它已经很受欢迎了。
于是你引入了一种特殊的杯子套,可以在每份咖啡上添加额外的装饰,比如奶泡、巧克力粉等。这样,每位顾客都可以根据自己的口味来定制自己的咖啡,而招牌咖啡的原味不会改变。
在这个比喻中:
- 咖啡馆是你的Python程序。
- 招牌咖啡是一个原始的函数或方法。
- 杯子套是装饰器,它允许你在不改变原函数代码的情况下,为其添加额外的功能或修改行为。
例如以下场景:
现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入 “before” 和 “after”。
def func(): print("我是func函数") value = (11,22,33,44) return value result = func() print(result)
按照基础的想法,我们可能会这样写
def func():
print("before")
print("我是func函数")
value = (11,22,33,44)
print("after")
return value
result = func()
但是运用装饰器后我们可以这样写
def func():
print("我是func函数")
value = (11, 22, 33, 44)
return value
def outer(origin):
def inner():
print('inner')
res = origin()
print("after")
return res
return inner
func = outer(func)
result = func()
当然,如果使用Python中的语法糖我们可以这样写
def outer(origin):
def inner():
print('inner')
res = origin()
print("after")
return res
return inner
@outer
def func():
print("我是func函数")
value = (11, 22, 33, 44)
return value
func()
下面我们接着看装饰器的另一个使用场景:
请在这3个函数执行前和执行后分别输入 “before” 和 “after”
def func1(): print("我是func1函数") value = (11, 22, 33, 44) return value def func2(): print("我是func2函数") value = (11, 22, 33, 44) return value def func3(): print("我是func3函数") value = (11, 22, 33, 44) return value func1() func2() func3()
当使用普通写法后:
def func1():
print('before')
print("我是func1函数")
value = (11, 22, 33, 44)
print("after")
return value
def func2():
print('before')
print("我是func2函数")
value = (11, 22, 33, 44)
print("after")
return value
def func3():
print('before')
print("我是func3函数")
value = (11, 22, 33, 44)
print("after")
return value
func1()
func2()
func3()
怎么样,当需要对多个函数添加一些相同的功能时,代码又出现了大量的重复,很显然,这和我们使用函数的初衷并不匹配。接下来我们再来看使用装饰器后的代码:
def outer(origin):
def inner():
print("before 110")
res = origin() # 调用原来的func函数
print("after")
return res
return inner
@outer
def func1():
print("我是func1函数")
value = (11, 22, 33, 44)
return value
@outer
def func2():
print("我是func2函数")
value = (11, 22, 33, 44)
return value
@outer
def func3():
print("我是func3函数")
value = (11, 22, 33, 44)
return value
func1()
func2()
func3()
装饰器,在不修改原函数内容的前提下,通过@函数可以实现在函数前后自定义执行一些功能而避免大量重复的代码(批量操作会更有意义)。
当然也会出现函数有参数的情况,我们当然可以直接在内层函数定义变量来传递,但是那样的话,如果传过来的函数参数不一致,固定的形参定义就会导致程序出错,所以我们通过*args和**kwargs这两个动态参数来接受所有传来的参数,再通过实参解压给对应的函数即可。
示例如下:
def outer(origin):
def inner(*args, **kwargs):
print("before 110")
res = origin(*args, **kwargs) # 调用原来的func函数
print("after")
return res
return inner
@outer # func1 = outer(func1)
def func1(a1):
print("我是func1函数")
value = (11, 22, 33, 44)
return value
@outer # func2 = outer(func2)
def func2(a1, a2):
print("我是func2函数")
value = (11, 22, 33, 44)
return value
@outer # func3 = outer(func3)
def func3(a1):
print("我是func3函数")
value = (11, 22, 33, 44)
return value
func1(1)
func2(11, a2=22)
func3(999)
到这里你可能会发现装饰器的本质,即:
装饰器实际上就是将原函数更改为其他的函数,然后再此函数中再去调用原函数。
def auth(func):
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
@auth
def handler():
pass
handler()
print(handler.__name__) # inner
装饰器总结
-
实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。
-
实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。
- 适用场景:装饰器在很多场景中非常有用,例如:
- 日志记录: 可以使用装饰器来记录函数的调用信息,如时间、参数等,以便调试或分析程序行为。
- 权限控制: 可以使用装饰器来验证用户是否有权限执行特定的操作,例如检查用户是否登录。
- 性能测试: 可以使用装饰器来测量函数的执行时间,以便优化代码性能。
- 缓存: 可以使用装饰器来缓存函数的返回值,以避免重复计算。