函数的装饰器:
(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