要想明白装饰器,得先明白闭包,所以什么是闭包呢?
闭包的原则:
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 add(a, b): return a+b
现在增加一个需求为:需要在增加乘法、除法、减法运算
def yunsuan(fun): def fuc(a, b): print(a * b) print(a - b) print(a/b) fun(a, b) return fuc
引用
def yunsuan(fun): def fuc(a, b): print(a * b) print(a - b) print(a/b) fun(a, b) return fuc @yunsuan def add(a, b): print(a+b)
那么有没有通用的呢,比如带参数的和不带参数的我只用一个装饰器来实现呢,下面有这样一个场景:一个展示首页的和一个展示商品页面的都需要先登录,但是展示首页的函数不需要参数,展示商品页面需要传递参数
比如:
def shouye(): print("这是首页") def shangpin_list(num): print("这是商品页第{}页".format(num))
上面可以看出第一个函数是不需要参数的,第二个函数需要一个参数
我们实现登陆功能的话怎么做呢,下面请看详细操作
def login(func): def fun(*args, **kwargs): username = "lc" password = "123456" user = input("请输入账号:") pwd = input("请输入密码:") if user == username and pwd == password: func(*args, **kwargs) # 注意下这里的参数前面的*不能去掉 else: print("密码错误") return fun
好了,装饰器写好了,下面就是需要装饰函数了
def login(func): def fun(*args, **kwargs): username = "lc" password = "123456" user = input("请输入账号:") pwd = input("请输入密码:") if user == username and pwd == password: func(*args, **kwargs) # 注意下这里的参数前面的*不能去掉 else: print("密码错误") return fun @login def shouye(): print("这是首页") @login def shangpin_list(num): print("这是商品页第{}页".format(num)) shouye() print("-------------------") shangpin_list(3)
看下打印的结果呢
请输入账号:lc 请输入密码:12345678 密码错误 ------------------- 请输入账号:lc 请输入密码:123456 这是商品页第3页
很明显,我们已经实现了带参数和不带参数通用的装饰器了
上面已经将函数的装饰器实现了,既然存在函数的装饰器,那肯定可以装饰类了,那么装饰类的装饰器怎么写呢?其实和装饰函数的装饰器差别不大
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、使用类实现装饰器
原文:https://www.cnblogs.com/LCboss/p/12508936.html