学习装饰器之前,请先把闭包函数学好,装饰器是闭包函数的应用
一:开放封闭原则
1:介绍开放封闭
所谓开放封闭原则就是软件实体应该对扩展开放,而对修改封闭。开放封闭原则是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化,降低耦合,而开放封闭原则正是对这一目标的最直接体现。
开放封闭原则主要体现在两个方面:
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立其工作,而不要对类进行任何修改。
2:为什么要用到开放封闭原则呢?
软件需求总是变化的,世界上没有一个软件的是不变的,因此对软件设计人员来说,必须在不需要对原有系统进行修改的情况下,实现灵活的系统扩展。
3:如何做到对扩展开放,对修改封闭呢?
实现开放封闭的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以对修改就是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。
对于违反这一原则的类,必须通过重构来进行改善。常用于实现的设计模式主要有Template Method模式和Strategy 模式。而封装变化,是实现这一原则的重要手段,将经常变化的状态封装为一个类。
二:简单的装饰器
说了这么多,终于到了我们的装饰器了。
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
1:为什么要使用装饰器呢?
装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展
装饰器的本质:就是一个闭包函数
2:装饰器
(当老板让我们对一个函数的功能添加一些功能时,这会是什么情况呢?如果我们在这个函数里面修改,这违背了封闭原则;因为已经封装的代码轻易是不能修改的。那么我们又要扩展功能,那么我们在写一个函数不就行了函数名不和原来函数名相同。可是我们已经把原来的函数接口给了客户,难道我们还要因为一次功能修改在通知客户接口修改了,这肯定不行。这就难办了。新增加功能不能再原来的函数上修改,也不能新建一个不同的函数名接口实现功能。)
我们的目的是不改接口,还能实现新增功能
################################################### def login(func):#新增的功能 user=input("user:>>>") pwd=input("pwd:>>>") if user=='lilz' and pwd == '123': print ("success") func() else: print ("error") def index(): print("this is index") login(index) ###################偷梁换柱:实现了新增功能,不修改原代码######################## def login(func):#新增的功能:装饰器函数 def inner(): user = input("user:>>>") pwd = input("pwd:>>>") if user == 'lilz' and pwd == '123': print ("success") func() else: print ("error") return inner #实现的关键点就是闭包函数能够应用外层函数的func def index(): #原函数 print("this is index") index=login(index) #此时的index已经不是原来的那个index了 index() #应用接口 ########################## 语法糖 ######################################### def login(func):#新增的功能:装饰器函数 def inner(): user = input("user:>>>") pwd = input("pwd:>>>") if user == 'lilz' and pwd == '123': print ("success") func() else: print ("error") return inner #实现的关键点就是闭包函数能够应用外层函数的func @login #语法糖,等同于index=login(index) def index(): #原函数 print("this is index") index()#引用
上面的语法糖看似巧妙,实际上源函数名已经发生改变,如何才能使得源函数名不发生改变呢?
三:带参数的被装饰器
import time def show_time(func): def wrapper(a,b): start_time=time.time() func(a,b) end_time=time.time() print('spend %s'%(end_time-start_time)) return wrapper @show_time #add=show_time(add) def add(a,b): time.sleep(1) print(a+b) add(2,4)
四:不定长参数
#***********************************不定长参数 import time def show_time(func): def wrapper(*args,**kwargs): start_time=time.time() func(*args,**kwargs) end_time=time.time() print('spend %s'%(end_time-start_time)) return wrapper @show_time #add=show_time(add) def add(*args,**kwargs): time.sleep(1) sum=0 for i in args: sum+=i print(sum) add(2,4,8,9)
补充
1:classonlymethod
classmethod:
对象.方法
类.方法
classonlymethod:
类.方法
对比可以看到 classonlymethod的目的是为了用 类.方法 来调用方法,禁止 对象.方法 的方式调用
Django类装饰器
参考他人博文:Yuan先生