要想明白装饰器,得先明白闭包,所以什么是闭包呢?
闭包的原则:
1、函数中嵌套函数
2、外层函数返回内层函数名
3、内层函数引用外层函数的非全局变量
闭包的作用:
实现对数据的锁定,提高稳定性
那么下面来实现一个闭包函数吧
1 def fun(): 2 a = 2 3 def add(): 4 print(a) 5 return add
那么可以看出以上函数是满足闭包的原则的
1、函数中嵌套函数
2、外层函数返回内层函数名
3、内层函数引用外层函数的非全局变量
所以上面的fun函数是一个闭包函数
现在已经理解了闭包了,那么接下来来说说装饰器
装饰器其实和闭包差不多,因为在实际的开发过程中,要遵守一个原则,那就是开放封闭原则,这是什么意思呢?也就是说已经实现的功能,我们不能去改变其内部代码结构,但是可以扩展,这就是开放封闭原则
列如,我们现在有一个展示首页的函数
1 def shouye(): 2 print("这个是网站的首页")
现在增加了一个需求,在展示首页之前必须得先登陆之后才能看得到首页,不然不能看到,由于开放封闭原则我们不能去动这个shouye函数了,所以只能采用装饰器来实现
所以我们需要写一个登陆的脚本
1 def login(func): 2 def fun(): 3 username = "lc" 4 password = "123456" 5 user = input("请输入账号:") 6 pwd = input("请输入密码:") 7 if user == username and pwd == password: 8 func() 9 else: 10 print("密码错误") 11 return fun
这个函数的注意点就是if后面的这个func()是login函数的参数,因为判断成功之后就要展示首页了,所以其实这里就是相当于那个shouye的函数了
- 不带参数的装饰器
现在这个装饰器已经实现了,那么接下来看看怎么用
def login(func): def fun(): username = "lc" password = "123456" user = input("请输入账号:") pwd = input("请输入密码:") if user == username and pwd == password: func() else: print("密码错误") return fun @login # 调用这个装饰器 def shouye(): print("这个是网站的首页")
这样我们其实就已经实现了一个简单的不带参数的装饰器了
- 带参数的装饰器
那么既然说不带参数的装饰器,那肯定还存在带参数的装饰器咯
我们再来举个列子
下面有一个函数
def get(): print("hahahaha")
实现的带参数的装饰器
def get_(a): def hun(fun): def func(*args, **kwargs): if a !=None: print("wo bu shi kong zhi") fun(*args, **kwargs) else: print("wo shi yi ge kong zhi") fun(*args, **kwargs) return func return hun
引用
@get_("hhhh") def get(): print("hahahaha")
由此可看出:带有参数的装饰器有三层函数嵌套,第一层为装饰器传递参数,第二层为传入被装饰对象,第三层为具体实现的扩展功能
- 装饰类的装饰器
上面已经将函数的装饰器实现了,既然存在函数的装饰器,那肯定可以装饰类了,那么装饰类的装饰器怎么写呢?其实和装饰函数的装饰器差别不大
def login(func): def fun(*args, **kwargs): username = "lc" password = "123456" user = input("请输入账号:") pwd = input("请输入密码:") if user == username and pwd == password: return func(*args, **kwargs) # 对比下这里与之前的有什么不同? else: print("密码错误") return fun
@login class Test(): def __init__(self): pass t = Test() print(t)
装饰类的装饰器与装饰函数的装饰器,其实大致是差不多的,只是装饰器里面的func需要return
- 类装饰器
下面再总结下怎么创建类装饰器,何为类装饰器,也就是通过类来实现装饰器
首先实现类装饰器,我们要用到一个魔术方法(__call__)
那么__call__方法是干嘛的?
简单说下:__call__这个方法可以让对象变为一个可调用对象,具体可以去百度看看
举个栗子:
class C: def __init__(self): print("cccccc") c = C() c()
这样的话直接调用c()肯定会报错
Traceback (most recent call last): File "E:/KTP/test/装饰器.py", line 132, in <module> c() TypeError: 'C' object is not callable
所以我们就要用到__call__方法了
class C: def __init__(self): print("cccccc") def __call__(self, *args, **kwargs): print("cccccc") c = C() c()
这样直接调用c()就完全没问题了
下面是实现类装饰器的代码
class B: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("这是装饰器") self.func(*args, **kwargs) @B # print_1 = B(print1) def print_1(num): print("XXXXXXXX{}".format(num))
这篇文章目前介绍了
1、闭包的定义
2、函数的装饰器(不带参数、带参数、通用)
3、装饰类的装饰器
4、使用类实现装饰器