函数的装饰器:
(1),被装饰的函数没有返回值:
1 def decorator(func): 2 def wrapper(arg): 3 t1 = time.perf_counter() 4 func(arg) 5 print(time.perf_counter()-t1) 6 return wrapper 7 8 @decorator 9 def func(arg): #没有返回值 10 i = 1 11 while(1): 12 i+=1 13 if i>1000: 14 print(arg) 15 break 16 17 18 if __name__ =="__main__": 19 import time 20 func("I am done") 21 22 ''' 23 输出: 24 I am done 25 0.00021631981540709086 26 '''
(2),被装饰的函数有返回值:
1 def decorator(func): 2 def wrapper(arg): 3 t1 = time.perf_counter() 4 i = func(arg) 5 print(time.perf_counter()-t1) 6 return i 7 return wrapper 8 9 @decorator 10 def func(arg): #有返回值 11 i = 1 12 while(1): 13 i+=1 14 if i>1000: 15 print(arg) 16 break 17 18 return i 19 20 21 if __name__ =="__main__": 22 import time 23 i = func("I am done") 24 print(i) 25 ''' 26 输出: 27 I am done 28 9.514658547491372e-05 29 1001 30 '''
内部原理:它利用的就是语法糖@:
@decorator 在一个函数的上方实际是:
func = decorator(func)
证明如下:
1 def decorator(func): 2 print("I am decorator") 3 4 @decorator #----> func = decorator(func) 5 def func(arg): 6 print("I am func") 7 8 9 if __name__ =="__main__": 10 pass 11 12 ''' 13 输出: 14 I am decorator 15 '''
类的装饰器:
它其实和函数装饰器是一样的,
1 def decorator(obj): #装饰器本质上是给对象装饰,obj,所以类对象(类名)也可以 2 print("-------->",obj) 3 obj.x = 1 #添加的类属性 4 obj.y = 2 5 return obj 6 7 @decorator 8 class DemoClass: 9 pass 10 11 12 if __name__ =="__main__": 13 demo = DemoClass() 14 print(DemoClass.__dict__) 15 print(demo.__dict__) 16 ''' 17 输出: 18 --------> <class '__main__.DemoClass'> 19 {'__dict__': <attribute '__dict__' of 'DemoClass' objects>, '__module__': '__main__', 'x': 1, '__doc__': None, 'y': 2, '__weakref__': <attribute '__weakref__' of 'DemoClass' objects>} 20 {} 21 '''
补:函数名也是个对象,如下:
1 def test(): 2 pass 3 test.x = 1 #理论上是可以这么干的,这说明了一切皆对象 4 test.y = 2 5 6 if __name__ =="__main__": 7 print(test.__dict__) 8 9 ''' 10 输出:{'x': 1, 'y': 2} 11 '''
而语法糖@的工作就是将它后面的对象作为参数传入,处理完之后再返回该对象。
继续类的装饰器:
1 def decorator(obj): 2 obj.x = 1 3 return obj 4 5 @decorator 6 class Democlass: 7 pass 8 9 10 if __name__ =="__main__": 11 pass 12 13 如果是这样的话,以后再想添加新的类属性就不方便了!
改进:
1 def decorator(**kwargs): 2 def deco(obj): #真正的装饰,装饰Democlass 3 for k,v in kwargs.items(): 4 # obj.__dict__[k]=v #会报错 TypeError: 'mappingproxy' object does not support item assignment 5 setattr(obj,k,v) #这种方式可以 6 return obj 7 return deco 8 9 @decorator(x=1,y=2,z=3) #1,先执行decorator(x=1,y=2,z=3) 2,返回结果再执行装饰Democlass 10 class Democlass: 11 pass 12 13 14 if __name__ =="__main__": 15 print(Democlass.__dict__) 16 17 ''' 18 输出: 19 {'__doc__': None, 'y': 2, 'x': 1, 'z': 3, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Democlass' objects>, '__weakref__': <attribute '__weakref__' of 'Democlass' objects>} 20 '''
这就是类的装饰器。
(装饰器就是个函数)
类的装饰器的应用:
配合描述符一起使用:(描述符的应用在这:https://www.cnblogs.com/zach0812/p/11312252.html)
class Person: name = Check("name",str) age = Check("age",int) salary = Check("salary",float) #它们三个都是给类增加属性,所以可以考虑通过类装饰器增加属性 def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary
1 def decorator(**kwargs): 2 def deco(obj): 3 for k,v in kwargs.items(): 4 setattr(obj,k,v) 5 return obj 6 return deco 7 8 class Check: 9 def __init__(self,key,type): 10 self.key = key 11 self.type= type 12 13 def __set__(self, instance, value): 14 print("set") 15 if isinstance(value,self.type): 16 instance.__dict__[self.key] = value 17 else: 18 print("{}输入有误".format(self.key)) 19 # raise TypeError("{}输入有误".format(self.key)) 20 def __get__(self, instance, owner): 21 print("get") 22 return instance.__dict__[self.key] 23 24 def __delete__(self, instance): 25 print("delete") 26 instance.__dict__.pop(self.key) 27 28 @decorator(name = Check("name",str),age = Check("age",int),salary = Check("salary",float)) 29 class Person: 30 def __init__(self,name,age,salary): 31 self.name = name 32 self.age = age 33 self.salary = salary 34 35 if __name__ =="__main__": 36 p1 = Person("tom",18,100.0) 37 print("============") 38 p2 = Person(108,"tom","jack") 39 ''' 40 输出: 41 set 42 set 43 set 44 ============ 45 set 46 name输入有误 47 set 48 age输入有误 49 set 50 salary输入有误 51 '''
终结版:
1 def decorator(**kwargs): 2 def deco(obj): 3 for k,v in kwargs.items(): 4 setattr(obj,k,Check(k,v)) 5 return obj 6 return deco 7 8 class Check: 9 def __init__(self,key,type): 10 self.key = key 11 self.type= type 12 13 def __set__(self, instance, value): 14 print("set") 15 if isinstance(value,self.type): 16 instance.__dict__[self.key] = value 17 else: 18 print("{}输入有误".format(self.key)) 19 # raise TypeError("{}输入有误".format(self.key)) 20 def __get__(self, instance, owner): 21 print("get") 22 return instance.__dict__[self.key] 23 24 def __delete__(self, instance): 25 print("delete") 26 instance.__dict__.pop(self.key) 27 28 @decorator(name = str,age = int,salary = float) #相对上一个,进一步简化代码 29 class Person: 30 def __init__(self,name,age,salary): 31 self.name = name 32 self.age = age 33 self.salary = salary 34 35 if __name__ =="__main__": 36 p1 = Person("tom",18,100.0) 37 print("============") 38 p2 = Person(108,"tom","jack") 39 ''' 40 输出: 41 set 42 set 43 set 44 ============ 45 set 46 name输入有误 47 set 48 age输入有误 49 set 50 salary输入有误 51 '''
这就是终结版的对类型检测的程序,
不过,我们也可以用@property 来进行检测。但是它不好,我们不用,但是这里要说下它!
1 class DemoClass: 2 def __init__(self): 3 pass 4 5 @property 6 def age(self): 7 return self._age 8 9 @age.setter 10 def age(self,val): 11 if val <0: 12 self._age = 30 13 else: 14 self._age = val 15 16 if __name__=="__main__": 17 demo = DemoClass() 18 demo.age = -50 19 print(demo.age)
property的使用(2):
1 class DemoClass: 2 def __init__(self,name): 3 self.name = name 4 5 @property 6 def age(self): 7 pass 8 9 @age.setter 10 def age(self,val): 11 # self.age = val #会构成递归 12 self.__dict__['age'] =val 13 @age.getter 14 def age(self): 15 # return self.age #递归 16 return self.__dict__['age'] 17 18 @age.deleter 19 def age(self): 20 # del self.age #递归 21 del self.__dict__['age'] 22 23 if __name__ == "__main__": 24 demo = DemoClass("tom") 25 demo.age = 18 26 print(demo.age) 27 del demo.age 28 ''' 29 输出: 30 18 31 {'age': 18, 'name': 'tom'} 32 '''
class DemoClass: def __init__(self,name): self.name = name @property # age = property(age) age 此时为property类 的对象 def age(self): print("2") return self.__dict__['age'] @age.setter def age(self,val): self.__dict__['age'] =val @age.getter def age(self): print("1") return self.__dict__['age'] @age.deleter def age(self): del self.__dict__['age'] if __name__ == "__main__": demo = DemoClass("tom") demo.age = 20 demo.age ''' 输出:1 ,有age.getter()时就不调property()了,没有的话会调它 '''
它之所以不好用,就是因为它不能复用,每要检测一个属性的取值范围都要新写一个。不像上面描述符写的灵活!
现在我们利用描述符自己手写个自制的property装饰器.
之前说的函数的装饰器和类的装饰器都是函数,其实类也可以作为装饰器。
1 class Myproperty: 2 def __init__(self,func): 3 self.func = func 4 5 class DemoClass: 6 def __init__(self,name): 7 self.name = name 8 @Myproperty # age = Myproperty(age) age 此时为Myproperty类 的对象 9 def age(self): #age 在类的属性字典中,是个类属性 10 print("hello") 11 12 if __name__ == "__main__": 13 demo = DemoClass("tom") 14 print(demo.__dict__) 15 print(DemoClass.__dict__) 16 17 ''' 18 输出: 19 {'name': 'tom'} 20 {'__dict__': <attribute '__dict__' of 'DemoClass' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'DemoClass' objects>, '__init__': <function DemoClass.__init__ at 0x000001A7732C3BF8>, '__module__': '__main__', 'age': <__main__.Myproperty object at 0x000001A7732BFBE0>} 21 '''
现在的age是个类属性了,我们可以通过描述符对它进行属性管理(让它成为property那种的不加括号就调用了def age())。
1 class Myproperty: 2 def __init__(self,func): 3 self.func = func 4 5 def __get__(self, instance, owner): #使Myproperty 成为非数据描述符 6 return self.func(instance) #instance 是省略的self 7 8 class DemoClass: 9 def __init__(self,name,width,length): 10 self.name = name 11 self.width = width 12 self.length = length 13 @Myproperty # area = Myproperty(area) 14 def area(self): 15 return self.width*self.length 16 17 18 if __name__ == "__main__": 19 demo = DemoClass("厕所",15,10) 20 print(demo.area) #实际上调的还是def area ()
@语法糖都是利用描述符的原理来做的,还可以自定制@classmethod 和 classstaticmethod
来源:https://www.cnblogs.com/zach0812/p/11312971.html