函数的装饰器和类的装饰器

寵の児 提交于 2020-03-19 04:42:35

 

函数的装饰器

(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 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!