装饰器就是闭包的一个很好的应用拓展!!!python
器指的是工具,而程序中的函数就是具有某一功能的工具,因此装饰器指的是为被装饰器对象添加额外功能。所以定义装饰器就是定义一个函数,只不过该函数的功能是用来为其余函数添加额外的功能。mysql
须要注意的是:sql
装饰器自己实际上是能够任意可调用的对象
被装饰的对象也能够是任意可调用的对象mongodb
若是咱们已经上线了一个项目,咱们须要修改某一个方法,可是咱们不想修改方法的使用方法,这个时候可使用装饰器。由于软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。闭包
装饰器的实现必须遵循两大原则:app
不修改被装饰对象的源代码
不修改被装饰对象的调用方式
装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。函数
改变源代码工具
import time def index(): start = time.time() print('welcome to index') time.sleep(1) end = time.time() print(F"index run time is {start-end}") index()
welcome to index index run time is -1.0008180141448975
编写重复代码.net
import time def index(): print('welcome to index') time.sleep(1) def f2(): print('welcome to index') time.sleep(1) start = time.time() index() end = time.time() print(F"index run time is {start-end}") start = time.time() f2() end = time.time() print(F"f2 run time is {start-end}")
welcome to index index run time is -1.0046868324279785 welcome to index f2 run time is -1.000690221786499
第一种传参方式:改变调用方式代理
import time def index(): print('welcome to index') time.sleep(1) def time_count(func): start = time.time() func() end = time.time() print(f"{func} time is {start-end}") time_count(index)
welcome to index <function index at 0x102977378> time is -1.000748872756958
第二种传参方式:包给函数-外包
import time def index(): print('welcome to index') time.sleep(1) def time_count(func): # func = 最原始的index def wrapper(): start = time.time() func() end = time.time() print(f"{func} time is {start-end}") return wrapper # f = time_count(index) # f() index = time_count(index) # index为被装饰函数的内存地址,即index = wrapper index() # wrapper()
welcome to index <function index at 0x102977730> time is -1.0038220882415771
上述的装饰器,最后调用index()的时候,实际上是在调用wrapper(),所以若是原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,咱们须要同步原始的index()和wrapper()方法的返回值。
import time def index(): print('welcome to index') time.sleep(1) return 123 def time_count(func): # func = 最原始的index def wrapper(): start = time.time() res = func() end = time.time() print(f"{func} time is {start-end}") return res return wrapper index = time_count(index) res = index() print(f"res: {res}")
welcome to index <function index at 0x102977620> time is -1.0050289630889893 res: 123
若是原始的index()方法须要传参,那么咱们以前的装饰器是没法实现该功能的,因为有wrapper()=index(),因此给wrapper()方法传参便可。
import time def index(): print('welcome to index') time.sleep(1) return 123 def home(name): print(f"welcome {name} to home page") time.sleep(1) return name def time_count(func): # func = 最原始的index def wrapper(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print(f"{func} time is {start-end}") return res return wrapper home = time_count(home) res = home('egon') print(f"res: {res}")
welcome egon to home page <function home at 0x102977378> time is -1.0039079189300537 res: egon
在被装饰函数正上方,而且是单独一行写上@装饰器名
import time def time_count(func): # func = 最原始的index def wrapper(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print(f"{func} time is {start-end}") return res return wrapper @time_count # home = time_count(home) def home(name): print(f"welcome {name} to home page") time.sleep(1) return name @time_count # index = time_count(index) def index(): print('welcome to index') time.sleep(1) return 123 res = home('egon') print(f"res: {res}")
welcome egon to home page <function home at 0x102977620> time is -1.0005171298980713 res: egon
def deco(func): def wrapper(*args,**kwargs): res = func(*args,**kwargs) return res return wrapper
通常参数器最多3层,也能够套更多层,但没有意义,3层足够装饰器自己的实现想要的功能添加的需求,固然也能够给某个想实现功能函数或类装饰更多的功能装饰器来实现不一样场景状况
提早说下哈...
无参装饰器只套了两层,本节将讲一个套三层的装饰器——有参装饰器,但如今咱们先实现一个用户登陆注册的装饰器。
import time current_user = {'username': None} def login(func): # func = 最原始的index def wrapper(*args, **kwargs): if current_user['username']: res = func(*args, **kwargs) return res user = input('username: ').strip() pwd = input('password: ').strip() if user == 'nash' and pwd == '123': print('login successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res else: print('user or password error') return wrapper @login def home(name): print(f"welcome {name} to home page") time.sleep(1) return name @login def index(): print('welcome to index') time.sleep(1) return 123 res = index()
username: nash password: 123 login successful welcome to index
对于上面的登陆注册,咱们把用户登陆成功的信息写入内存当中。可是在工业上,用户信息能够存在文本中、mysql中、mongodb当中,可是咱们只让用户信息来自于file的用户能够认证。所以咱们能够改写上述的装饰器。
import time current_user = {'username': None} def login(func): # func = 最原始的index def wrapper(*args, **kwargs): if current_user['username']: res = func(*args, **kwargs) return res user = input('username: ').strip() pwd = input('password: ').strip() engine = 'file' if engine == 'file': print('base of file') if user == 'nash' and pwd == '123': print('login successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res else: print('user or password error') elif engine == 'mysql': print('base of mysql') elif engine == 'mongodb': print('base of mongodb') else: print('default') return wrapper @login def home(name): print(f"welcome {name} to home page") time.sleep(1) @login def index(): print('welcome to index') time.sleep(1) res = index()
username: nash password: 123 base of file login successful welcome to index
def f1(y): def f2(): x = 1 def f3(): print(f"x: {x}") print(f"y: {y}") return f3 return f2 f2 = f1(2) f3 = f2() f3()
x: 1 y: 2
如今需求改了,咱们须要判断用户动态的获取用户密码的方式,若是是file类型的,咱们则让用户进行认证。所以咱们可使用有参装饰器。
import time current_uesr = {'username': None} def auth(engine='file'): def login(func): # func = 最原始的index def wrapper(*args, **kwargs): if current_user['username']: res = func(*args, **kwargs) return res user = input('username: ').strip() pwd = input('password: ').strip() if engine == 'file': print('base of file') if user == 'nash' and pwd == '123': print('login successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res else: print('user or password error') elif engine == 'mysql': print('base of mysql, please base of file') elif engine == 'mongodb': print('base of mongodb, please base of file') else: print('please base of file') return wrapper return login @auth(engine='mysql') def home(name): print(f"welcome {name} to home page") time.sleep(1) @auth(engine='file') def index(): print('welcome to index') time.sleep(1) res = index()
username: nash password: 123 base of file login successful welcome to index
因为两层的装饰器,参数必须得固定位func,可是三层的装饰器解除了这个限制。咱们不只仅可使用上述单个参数的三层装饰器,多个参数的只须要在三层装饰器中多加入几个参数便可。也就是说装饰器三层便可,多加一层反倒无用。
回顾什么是装饰器:
装饰器定义:本质就是函数(高阶函数),功能是为其余函数(对象)添加新功能
1、类的装饰器基本实现原理以下:
def deco(cls): print('类的装饰器=========》') print('='*20) return cls @deco #====> Foo = deco(Foo) class Foo: pass
2、上述的简单装饰器是没有参数的,如今咱们加上参数
def cls_decorator(**kwargs): #支持传入参数(属性和对应的值),字典形式 def deco(cls): for key,val in kwargs.items(): setattr(cls,key,val) #给类设置对应的属性和值 return cls return deco @cls_decorator(name='Menawey',age=24,gender='male') # 1 运行cls_decorator(...),返回deco;2 @deco===> Peolple = deco(People) #至关于给People类设置了name、age、gender属性 class People: pass print(People.__dict__) #查看被装饰过的类的属性字典
经过这样就能够动态的给不一样的类在他实例化前增长属性
3、结合描述符
经过描述符和类的装饰器组合使用,能够完成不少功能,好比为类添加属性,而且能够限定属性的类型。
#描述符 class Desc: def __init__(self,key,value_type): self.key = key self.value_type = value_type def __get__(self, instance, owner): return instance.__dict__[self.key] def __set__(self, instance, value): if not isinstance(value,self.value_type): raise TypeError('%s 传入的类型不是 %s'%(self.key,self.value_type)) instance.__dict__[self.key] = value def __delete__(self, instance): instance.__dict__.pop(self.key) #装饰器 def cls_decorator(**kwargs): #支持传入参数(属性和对应的值),字典形式 def deco(cls): for key,val in kwargs.items(): #这里须要用到描述符对属性进行代理,可是val是指定的类型,因此要用Desc(key,val)来描述为对应的值 setattr(cls,key,Desc(key,val)) #给类设置对应的属性和值 return cls return deco @cls_decorator(name=str,age=int,gender=str,salary=float) #使用装饰器 #被装饰和描述的类 class People: def __init__(self,name,age,gender,salary): self.name = name self.age = age self.gender = gender self.salary = salary p1 = People('Menawey',24,'male',11.1) #由于gender属性指定的是sstr,可是TypeError: age 传入的类型不是 <class 'int'> print(People.__dict__)
What??!! python中类做为装饰器!!!??
参考连接:
https://blog.csdn.net/qq_29767317/article/details/80799410
da #重写了__call__方法,类的对象就可以被调用,直接使用t()来调用类,并打印__call__方法里面的内容 class Test(object): print("--------test1----------") def __call__(self, *args, **kwargs): print(da) #注意一点,__call__方法里面的是最后执行。 def eat(self): print("-----------eat-------------") print("---------test2---------") t = Test() t()
执行结果:
D:\python3.6\pythonw.exe F:/python项目/09Day/03python高级/01类做为装饰器.py
--------test1----------
---------test2---------
---------test---------
类作为装饰器:
class Test(object): def __init__(self,func): print("-------初始化-------") print("func name is %s" %func.__name__) self.__func = func #类的私有属性self.__func也指向了test1函数的内存地址。 def __call__(self, *args, **kwargs): #test1 = Test(test1) #调用类的对象。就会调用call方法。 print("------装饰器中的功能-------") self.__func() #self.__func指向了函数test1的内存地址。这句话至关于执行test1()函数。 #使用类做为装饰器,须要重写Call方法,没有调用test1()方法的时候,执行代码获得下面的结果 # -------初始化------- # func name is test1 @Test #至关于 test1 = Test(test1) 也至关于func指向了下面函数test1的名字, 前面的test1指向了 Test()这个对象。 # 调用test1对象的时候至关于调用类方法Test(),调用类方法必调用__call方法,调用call方法的时候,先执行 print("------装饰器中的功能-------") #而后在执行self.__func() ,由于self.__func函数指向的是test1函数,test1()至关于执行self.__func(). def test1(): print("----test1---------") test1() #调用test1
D:\python3.6\pythonw.exe F:/python项目/09Day/03python高级/02类做为装饰器.py -------初始化------- func name is test1 ------装饰器中的功能------- ----test1---------